diff --git a/ChangeLog b/ChangeLog index 388e0ccc9b62371e1f041dc0967d0f9b11eeff05..06d729e24c301b2e4bb31b6ca8222f0f72bdef4f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +1634. [func] franek, razvan + Kea statistics improvements: Added commands for set sample age and + set sample count. + (Gitlab #731,!459, git dde1b96b33ed20dbe2c815f1168e62b66635e39f) + 1633. [bug] fdupont Added missing YANG Kea test module in distributions. (Gitlab #747,!436, git a800e79c7917acc723cbc71b626adc360e15a8d7) diff --git a/doc/sphinx/api/cmds-list b/doc/sphinx/api/cmds-list index 0289e4d431309b6a288622128684c89666e7336d..bcfaf87c3779a66f484e69c5e094b5cba0fad29f 100644 --- a/doc/sphinx/api/cmds-list +++ b/doc/sphinx/api/cmds-list @@ -117,6 +117,10 @@ statistic-remove statistic-remove-all statistic-reset statistic-reset-all +statistic-sample-age-set +statistic-sample-age-set-all +statistic-sample-count-set +statistic-sample-count-set-all subnet4-add subnet4-del subnet4-get diff --git a/doc/sphinx/api/statistic-sample-age-set-all.json b/doc/sphinx/api/statistic-sample-age-set-all.json new file mode 100644 index 0000000000000000000000000000000000000000..09f172b07410dd7f49212fe8db140682b10acd79 --- /dev/null +++ b/doc/sphinx/api/statistic-sample-age-set-all.json @@ -0,0 +1,14 @@ +{ + "name": "statistic-sample-age-set-all", + "brief": "The statistic-sample-age-set-all command sets time based limit for all statistics. It takes a single integer parameter called duration.", + "description": "See ", + "support": [ "kea-dhcp4", "kea-dhcp6" ], + "avail": "1.6.0", + "cmd-syntax": "{ + \"command\": \"statistic-sample-age-set-all\", + \"arguments\": { + \"duration\": 1245 + } +}", + "cmd-comment": "The server will respond with message about successfully set limits for all statistics, with a result set to 0 indicating success and an empty parameters field. If an error is encountered, the server will return a status code of 1 (error) and the text field will contain the error description." +} diff --git a/doc/sphinx/api/statistic-sample-age-set.json b/doc/sphinx/api/statistic-sample-age-set.json new file mode 100644 index 0000000000000000000000000000000000000000..42d22bdd3cb179d8257b5745cc5be665caa48222 --- /dev/null +++ b/doc/sphinx/api/statistic-sample-age-set.json @@ -0,0 +1,15 @@ +{ + "name": "statistic-sample-age-set", + "brief": "The statistic-sample-age-set command sets time based limit for single statistic. It takes two parameters: a string called name and an integer value called duration.", + "description": "See ", + "support": [ "kea-dhcp4", "kea-dhcp6" ], + "avail": "1.6.0", + "cmd-syntax": "{ + \"command\": \"statistic-sample-age-set\", + \"arguments\": { + \"name\": \"pkt4-received\", + \"duration\": 1245 + } +}", + "cmd-comment": "The server will respond with message about successfully set limit for the given statistic, with a result set to 0 indicating success and an empty parameters field. If an error is encountered (e.g. requested statistic was not found), the server will return a status code of 1 (error) and the text field will contain the error description." +} diff --git a/doc/sphinx/api/statistic-sample-count-set-all.json b/doc/sphinx/api/statistic-sample-count-set-all.json new file mode 100644 index 0000000000000000000000000000000000000000..ef7ba348097803c6ff230fdb7dcf0fd1b2c853fb --- /dev/null +++ b/doc/sphinx/api/statistic-sample-count-set-all.json @@ -0,0 +1,14 @@ +{ + "name": "statistic-sample-count-set-all", + "brief": "The statistic-sample-count-set-all command sets size based limit for all statistics. It takes a single integer parameter called max-samples.", + "description": "See ", + "support": [ "kea-dhcp4", "kea-dhcp6" ], + "avail": "1.6.0", + "cmd-syntax": "{ + \"command\": \"statistic-sample-count-set-all\", + \"arguments\": { + \"max-samples\": 100 + } +}", + "cmd-comment": "The server will respond with message about successfully set limits for all statistics, with a result set to 0 indicating success and an empty parameters field. If an error is encountered, the server will return a status code of 1 (error) and the text field will contain the error description." +} diff --git a/doc/sphinx/api/statistic-sample-count-set.json b/doc/sphinx/api/statistic-sample-count-set.json new file mode 100644 index 0000000000000000000000000000000000000000..7f9ad07103dda1f66cbb88c3f745d34740ed3a81 --- /dev/null +++ b/doc/sphinx/api/statistic-sample-count-set.json @@ -0,0 +1,15 @@ +{ + "name": "statistic-sample-count-set", + "brief": "The statistic-sample-count-set command sets size based limit for single statistic. It takes two parameters: a string called name and an integer value called max-samples.", + "description": "See ", + "support": [ "kea-dhcp4", "kea-dhcp6" ], + "avail": "1.6.0", + "cmd-syntax": "{ + \"command\": \"statistic-sample-count-set\", + \"arguments\": { + \"name\": \"pkt4-received\", + \"max-samples\": 100 + } +}", + "cmd-comment": "The server will respond with message about successfully set limit for the given statistic, with a result set to 0 indicating success and an empty parameters field. If an error is encountered (e.g. requested statistic was not found), the server will return a status code of 1 (error) and the text field will contain the error description." +} diff --git a/doc/sphinx/arm/stats.rst b/doc/sphinx/arm/stats.rst index 139504774ff111a971a750da4315dedb7e0ca40e..88e5d2d81cfe3b0f6a5e5a8679b4aad059457c67 100644 --- a/doc/sphinx/arm/stats.rst +++ b/doc/sphinx/arm/stats.rst @@ -233,3 +233,130 @@ If the removal of all statistics is successful, the server responds with a status of 0, indicating success, and an empty parameters field. If an error is encountered, the server returns a status code of 1 (error) and the text field contains the error description. + +.. _command-statistic-sample-age-set: + +The statistic-sample-age-set Command +---------------------------------------- + +The ``statistic-sample-age-set`` command sets time based limit +for collecting samples for given statistic. An example command may look +like this: + +:: + + { + "command": "statistic-sample-age-set", + "arguments": { + "name": "pkt4-received", + "duration": 1245 + } + + } + +The server will respond with message about successfully set limit +for the given statistic, with a result set to 0 indicating success +and an empty parameters field. If an error is encountered (e.g. the +requested statistic was not found), the server returns a status code +of 1 (error) and the text field contains the error description. + +.. _command-statistic-sample-age-set-all: + +The statistic-sample-age-set-all Command +-------------------------------------------- + +The ``statistic-sample-age-set-all`` command sets time based limits +for collecting samples for all statistics. An example command may look +like this: + +:: + + { + "command": "statistic-sample-age-set-all", + "arguments": { + "duration": 1245 + } + + } + +The server will respond with message about successfully set limit +for all statistics, with a result set to 0 indicating success +and an empty parameters field. If an error is encountered, the server returns +a status code of 1 (error) and the text field contains the error description. + +.. _command-statistic-sample-count-set: + +The statistic-sample-count-set Command +------------------------------------------ + +The ``statistic-sample-count-set`` command sets size based limit +for collecting samples for given statistic. An example command may look +like this: + +:: + + { + "command": "statistic-sample-count-set", + "arguments": { + "name": "pkt4-received", + "max-samples": 100 + } + + } + +The server will respond with message about successfully set limit +for the given statistic, with a result set to 0 indicating success +and an empty parameters field. If an error is encountered (e.g. the +requested statistic was not found), the server returns a status code +of 1 (error) and the text field contains the error description. + +.. _command-statistic-sample-count-set-all: + +The statistic-sample-count-set-all Command +---------------------------------------------- + +The ``statistic-sample-count-set-all`` command sets size based limits +for collecting samples for all statistics. An example command may look +like this: + +:: + + { + "command": "statistic-sample-count-set-all", + "arguments": { + "max-samples": 100 + } + + } + +The server will respond with message about successfully set limit +for all statistics, with a result set to 0 indicating success +and an empty parameters field. If an error is encountered, the server returns +a status code of 1 (error) and the text field contains the error description. + +.. _time-series: + +Time series +==================== + +Previously, by default, each statistic held only a single data point. When Kea +attempted to record a new value, the existing previous value was overwritten. +That approach has the benefit of taking up little memory and it covers most +cases reasonably well. However, there may be cases where you need to have many +data points for some process. For example, some processes, such as received +packet size, packet processing time or number of database queries needed to +process a packet, are not cumulative and it would be useful to keep many data +points, perhaps to do some form of statistical analysis afterwards. + + +Since Kea 1.6, by default, each statistic holds 20 data points. Setting such +limit prevent unlimited memory consumption growth. +There are two ways to define the limts: time based (e.g. keep samples from +the last 5 minutes) and size based. It's possible to change the size based +limit by using one of two commands: ``statistic-sample-count-set``, +to set size limit for single statistic and ``statistic-sample-count-set-all`` +for setting size based limits for all statistics. To set time based +limit for single statistic use ``statistic-sample-age-set``, and +``statistic-sample-age-set-all`` to set time based limits for all statistics. +For given statistic only one type of limit can be active. It means that storage +is limited only by time based limit or size based, never by both of them. diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index b74f149f7d225343151fb263c3883977398665c3..4987613534b02ad50388f2f137a2f9660b7c7990 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -864,6 +864,17 @@ ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_P CommandMgr::instance().registerCommand("statistic-remove-all", boost::bind(&StatsMgr::statisticRemoveAllHandler, _1, _2)); + CommandMgr::instance().registerCommand("statistic-sample-age-set", + boost::bind(&StatsMgr::statisticSetMaxSampleAgeHandler, _1, _2)); + + CommandMgr::instance().registerCommand("statistic-sample-age-set-all", + boost::bind(&StatsMgr::statisticSetMaxSampleAgeAllHandler, _1, _2)); + + CommandMgr::instance().registerCommand("statistic-sample-count-set", + boost::bind(&StatsMgr::statisticSetMaxSampleCountHandler, _1, _2)); + + CommandMgr::instance().registerCommand("statistic-sample-count-set-all", + boost::bind(&StatsMgr::statisticSetMaxSampleCountAllHandler, _1, _2)); } void ControlledDhcpv4Srv::shutdown() { @@ -903,6 +914,10 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { CommandMgr::instance().deregisterCommand("statistic-remove-all"); CommandMgr::instance().deregisterCommand("statistic-reset"); CommandMgr::instance().deregisterCommand("statistic-reset-all"); + CommandMgr::instance().deregisterCommand("statistic-sample-age-set"); + CommandMgr::instance().deregisterCommand("statistic-sample-age-set-all"); + CommandMgr::instance().deregisterCommand("statistic-sample-count-set"); + CommandMgr::instance().deregisterCommand("statistic-sample-count-set-all"); CommandMgr::instance().deregisterCommand("version-get"); } catch (...) { diff --git a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc index 9a0cc3ee63fc7c28d96412f608a66cd99aaa4c95..14439e52a21dcad48e407374d090a70d9a4e035b 100644 --- a/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc @@ -491,6 +491,10 @@ TEST_F(CtrlChannelDhcpv4SrvTest, commandsRegistration) { EXPECT_TRUE(command_list.find("\"statistic-remove-all\"") != string::npos); EXPECT_TRUE(command_list.find("\"statistic-reset\"") != string::npos); EXPECT_TRUE(command_list.find("\"statistic-reset-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-age-set\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-age-set-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-count-set\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-count-set-all\"") != string::npos); EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos); // Ok, and now delete the server. It should deregister its commands. @@ -709,6 +713,34 @@ TEST_F(CtrlChannelDhcpv4SrvTest, controlChannelStats) { " \"arguments\": {}}", response); EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }", response); + + // Check statistic-sample-age-set + sendUnixCommand("{ \"command\" : \"statistic-sample-age-set\", " + " \"arguments\": {" + " \"name\":\"bogus\", \"duration\": 1245 }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); + + // Check statistic-sample-age-set-all + sendUnixCommand("{ \"command\" : \"statistic-sample-age-set-all\", " + " \"arguments\": {" + " \"duration\": 1245 }}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics duration limit are set.\" }", + response); + + // Check statistic-sample-count-set + sendUnixCommand("{ \"command\" : \"statistic-sample-count-set\", " + " \"arguments\": {" + " \"name\":\"bogus\", \"max-samples\": 100 }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); + + // Check statistic-sample-count-set-all + sendUnixCommand("{ \"command\" : \"statistic-sample-count-set-all\", " + " \"arguments\": {" + " \"max-samples\": 100 }}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics count limit are set.\" }", + response); } // Check that the "config-set" command will replace current configuration @@ -883,6 +915,10 @@ TEST_F(CtrlChannelDhcpv4SrvTest, listCommands) { checkListCommands(rsp, "statistic-remove-all"); checkListCommands(rsp, "statistic-reset"); checkListCommands(rsp, "statistic-reset-all"); + checkListCommands(rsp, "statistic-sample-age-set"); + checkListCommands(rsp, "statistic-sample-age-set-all"); + checkListCommands(rsp, "statistic-sample-count-set"); + checkListCommands(rsp, "statistic-sample-count-set-all"); checkListCommands(rsp, "version-get"); } diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index aba2e9b091ec71759f3d7735bf44e42d714831fc..9e5947606cf24426de9b0ef140d366524d3fc9a0 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -887,6 +887,18 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port, CommandMgr::instance().registerCommand("statistic-remove-all", boost::bind(&StatsMgr::statisticRemoveAllHandler, _1, _2)); + + CommandMgr::instance().registerCommand("statistic-sample-age-set", + boost::bind(&StatsMgr::statisticSetMaxSampleAgeHandler, _1, _2)); + + CommandMgr::instance().registerCommand("statistic-sample-age-set-all", + boost::bind(&StatsMgr::statisticSetMaxSampleAgeAllHandler, _1, _2)); + + CommandMgr::instance().registerCommand("statistic-sample-count-set", + boost::bind(&StatsMgr::statisticSetMaxSampleCountHandler, _1, _2)); + + CommandMgr::instance().registerCommand("statistic-sample-count-set-all", + boost::bind(&StatsMgr::statisticSetMaxSampleCountAllHandler, _1, _2)); } void ControlledDhcpv6Srv::shutdown() { @@ -926,6 +938,10 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { CommandMgr::instance().deregisterCommand("statistic-remove-all"); CommandMgr::instance().deregisterCommand("statistic-reset"); CommandMgr::instance().deregisterCommand("statistic-reset-all"); + CommandMgr::instance().deregisterCommand("statistic-sample-age-set"); + CommandMgr::instance().deregisterCommand("statistic-sample-age-set-all"); + CommandMgr::instance().deregisterCommand("statistic-sample-count-set"); + CommandMgr::instance().deregisterCommand("statistic-sample-count-set-all"); CommandMgr::instance().deregisterCommand("version-get"); } catch (...) { diff --git a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc index d0dfb536e86649b9d63137a7224a74d1647ebe46..ee821e37a4e9f2399f8168b5ccb50bf91ed7eb16 100644 --- a/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc +++ b/src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc @@ -798,6 +798,10 @@ TEST_F(CtrlDhcpv6SrvTest, commandsRegistration) { EXPECT_TRUE(command_list.find("\"statistic-remove-all\"") != string::npos); EXPECT_TRUE(command_list.find("\"statistic-reset\"") != string::npos); EXPECT_TRUE(command_list.find("\"statistic-reset-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-age-set\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-age-set-all\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-count-set\"") != string::npos); + EXPECT_TRUE(command_list.find("\"statistic-sample-count-set-all\"") != string::npos); EXPECT_TRUE(command_list.find("\"version-get\"") != string::npos); // Ok, and now delete the server. It should deregister its commands. @@ -1023,6 +1027,34 @@ TEST_F(CtrlChannelDhcpv6SrvTest, controlChannelStats) { " \"arguments\": {}}", response); EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics removed.\" }", response); + + // Check statistic-sample-age-set + sendUnixCommand("{ \"command\" : \"statistic-sample-age-set\", " + " \"arguments\": {" + " \"name\":\"bogus\", \"duration\": 1245 }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); + + // Check statistic-sample-age-set-all + sendUnixCommand("{ \"command\" : \"statistic-sample-age-set-all\", " + " \"arguments\": {" + " \"duration\": 1245 }}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics duration limit are set.\" }", + response); + + // Check statistic-sample-count-set + sendUnixCommand("{ \"command\" : \"statistic-sample-count-set\", " + " \"arguments\": {" + " \"name\":\"bogus\", \"max-samples\": 100 }}", response); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'bogus' statistic found\" }", + response); + + // Check statistic-sample-count-set-all + sendUnixCommand("{ \"command\" : \"statistic-sample-count-set-all\", " + " \"arguments\": {" + " \"max-samples\": 100 }}", response); + EXPECT_EQ("{ \"result\": 0, \"text\": \"All statistics count limit are set.\" }", + response); } // Tests that the server properly responds to shtudown command sent @@ -1054,6 +1086,10 @@ TEST_F(CtrlChannelDhcpv6SrvTest, commandsList) { checkListCommands(rsp, "statistic-remove-all"); checkListCommands(rsp, "statistic-reset"); checkListCommands(rsp, "statistic-reset-all"); + checkListCommands(rsp, "statistic-sample-age-set"); + checkListCommands(rsp, "statistic-sample-age-set-all"); + checkListCommands(rsp, "statistic-sample-count-set"); + checkListCommands(rsp, "statistic-sample-count-set-all"); } // Tests if the server returns its configuration using config-get. diff --git a/src/lib/stats/observation.cc b/src/lib/stats/observation.cc index b790e8eb5b7973c4a5a9df4ca8d837eb873ab095..33661ffa9d92227e3ac7522e9f159bb57f2abc09 100644 --- a/src/lib/stats/observation.cc +++ b/src/lib/stats/observation.cc @@ -150,6 +150,14 @@ size_t Observation::getSize() const { return (size); } +std::pair Observation::getMaxSampleAge() const { + return (max_sample_age_); +} + +std::pair Observation::getMaxSampleCount() const { + return (max_sample_count_); +} + template size_t Observation::getSizeInternal(StorageType& storage, Type exp_type) const { if (type_ != exp_type) { @@ -178,8 +186,8 @@ void Observation::setValueInternal(SampleType value, StorageType& storage, storage.push_front(make_pair(value, microsec_clock::local_time())); if (max_sample_count_.first) { - // if max_sample_count is set to true - // and size of storage is equal to max_sample_count + // if max_sample_count_ is set to true + // and size of storage is equal to max_sample_count_ if (storage.size() > max_sample_count_.second) { storage.pop_back(); // removing the last element } @@ -273,10 +281,10 @@ void Observation::setMaxSampleAgeInternal(StorageType& storage, << typeToText(exp_type) << ", but the actual type is " << typeToText(type_)); } - // setting new value of max_sample_age + // setting new value of max_sample_age_ max_sample_age_.first = true; max_sample_age_.second = duration; - // deactivating the max_sample_count limit + // deactivating the max_sample_count_ limit max_sample_count_.first = false; StatsDuration range_of_storage = @@ -298,10 +306,10 @@ void Observation::setMaxSampleCountInternal(StorageType& storage, << typeToText(exp_type) << ", but the actual type is " << typeToText(type_)); } - // setting new value of max_sample_count + // setting new value of max_sample_count_ max_sample_count_.first = true; max_sample_count_.second = max_samples; - // deactivating the max_sample_age limit + // deactivating the max_sample_age_ limit max_sample_age_.first = false; while (storage.size() > max_samples) { diff --git a/src/lib/stats/observation.h b/src/lib/stats/observation.h index 23fd7ef23e04e497344c544fb4dc857fde5661bb..d979500554822142083d3eed96b6399e0ed3fbcd 100644 --- a/src/lib/stats/observation.h +++ b/src/lib/stats/observation.h @@ -66,8 +66,7 @@ typedef std::pair StringSample; /// @ref getDuration, @ref getString (appropriate type must be used) or /// @ref getJSON, which is generic and can be used for all types. /// -/// @todo: Eventually it will be possible to retain multiple samples for the same -/// observation, but that is outside of scope for 0.9.2. +/// Since Kea 1.6 multiple samples are stored for the same observation. class Observation { public: @@ -194,6 +193,16 @@ class Observation { /// @return size of storage size_t getSize() const; + /// @brief Returns both values of max_sample_age_ of statistic. + /// + /// @return max_sample_age_. + std::pair getMaxSampleAge() const; + + /// @brief Returns both values of max_sample_count_ of statistic. + /// + /// @return max_sample_count_. + std::pair getMaxSampleCount() const; + /// @brief Resets statistic. /// /// Sets statistic to a neutral (0, 0.0 or "") value and diff --git a/src/lib/stats/stats_mgr.cc b/src/lib/stats/stats_mgr.cc index eb2500ce345e4bc86e33ae0af57a5be690fdb844..25ad50086f95dd1bde6a54702a8148b7dfdbfaf4 100644 --- a/src/lib/stats/stats_mgr.cc +++ b/src/lib/stats/stats_mgr.cc @@ -101,6 +101,26 @@ bool StatsMgr::setMaxSampleCount(const std::string& name, } } +void StatsMgr::setMaxSampleAgeAll(const StatsDuration& duration) { + // Let's iterate over all stored statistics... + for (std::map::iterator s = global_->stats_.begin(); + s != global_->stats_.end(); ++s) { + + // ... and set duration limit for each statistic. + s->second->setMaxSampleAge(duration); + } +} + +void StatsMgr::setMaxSampleCountAll(uint32_t max_samples) { + // Let's iterate over all stored statistics... + for (std::map::iterator s = global_->stats_.begin(); + s != global_->stats_.end(); ++s) { + + // ... and set count limit for each statistic. + s->second->setMaxSampleCount(max_samples); + } +} + bool StatsMgr::reset(const std::string& name) { ObservationPtr obs = getObservation(name); if (obs) { @@ -164,26 +184,66 @@ size_t StatsMgr::count() const { return (global_->stats_.size()); } +isc::data::ConstElementPtr +StatsMgr::statisticSetMaxSampleAgeHandler(const std::string& /*name*/, + const isc::data::ConstElementPtr& params) { + std::string name, error; + StatsDuration duration; + if (!StatsMgr::getStatName(params, name, error)) { + return (createAnswer(CONTROL_RESULT_ERROR, error)); + } + if (!StatsMgr::getStatDuration(params, duration, error)) { + return (createAnswer(CONTROL_RESULT_ERROR, error)); + } + if (StatsMgr::instance().setMaxSampleAge(name, duration)) { + return (createAnswer(CONTROL_RESULT_SUCCESS, + "Statistic '" + name + "' duration limit is set.")); + } else { + return (createAnswer(CONTROL_RESULT_ERROR, + "No '" + name + "' statistic found")); + } +} + +isc::data::ConstElementPtr +StatsMgr::statisticSetMaxSampleCountHandler(const std::string& /*name*/, + const isc::data::ConstElementPtr& params) { + std::string name, error; + uint32_t max_samples; + if (!StatsMgr::getStatName(params, name, error)) { + return (createAnswer(CONTROL_RESULT_ERROR, error)); + } + if (!StatsMgr::getStatMaxSamples(params, max_samples, error)) { + return (createAnswer(CONTROL_RESULT_ERROR, error)); + } + if (StatsMgr::instance().setMaxSampleCount(name, max_samples)) { + return (createAnswer(CONTROL_RESULT_SUCCESS, + "Statistic '" + name + "' count limit is set.")); + } else { + return (createAnswer(CONTROL_RESULT_ERROR, + "No '" + name + "' statistic found")); + } +} + isc::data::ConstElementPtr StatsMgr::statisticGetHandler(const std::string& /*name*/, const isc::data::ConstElementPtr& params) { std::string name, error; - if (!getStatName(params, name, error)) { + if (!StatsMgr::getStatName(params, name, error)) { return (createAnswer(CONTROL_RESULT_ERROR, error)); } return (createAnswer(CONTROL_RESULT_SUCCESS, - instance().get(name))); + StatsMgr::instance().get(name))); } isc::data::ConstElementPtr StatsMgr::statisticResetHandler(const std::string& /*name*/, const isc::data::ConstElementPtr& params) { std::string name, error; - if (!getStatName(params, name, error)) { + if (!StatsMgr::getStatName(params, name, error)) { return (createAnswer(CONTROL_RESULT_ERROR, error)); } - if (instance().reset(name)) { + if (StatsMgr::instance().reset(name)) { return (createAnswer(CONTROL_RESULT_SUCCESS, "Statistic '" + name + "' reset.")); } else { @@ -196,10 +256,10 @@ isc::data::ConstElementPtr StatsMgr::statisticRemoveHandler(const std::string& /*name*/, const isc::data::ConstElementPtr& params) { std::string name, error; - if (!getStatName(params, name, error)) { + if (!StatsMgr::getStatName(params, name, error)) { return (createAnswer(CONTROL_RESULT_ERROR, error)); } - if (instance().del(name)) { + if (StatsMgr::instance().del(name)) { return (createAnswer(CONTROL_RESULT_SUCCESS, "Statistic '" + name + "' removed.")); } else { @@ -212,7 +272,7 @@ StatsMgr::statisticRemoveHandler(const std::string& /*name*/, isc::data::ConstElementPtr StatsMgr::statisticRemoveAllHandler(const std::string& /*name*/, const isc::data::ConstElementPtr& /*params*/) { - instance().removeAll(); + StatsMgr::instance().removeAll(); return (createAnswer(CONTROL_RESULT_SUCCESS, "All statistics removed.")); } @@ -220,18 +280,44 @@ StatsMgr::statisticRemoveAllHandler(const std::string& /*name*/, isc::data::ConstElementPtr StatsMgr::statisticGetAllHandler(const std::string& /*name*/, const isc::data::ConstElementPtr& /*params*/) { - ConstElementPtr all_stats = instance().getAll(); + ConstElementPtr all_stats = StatsMgr::instance().getAll(); return (createAnswer(CONTROL_RESULT_SUCCESS, all_stats)); } isc::data::ConstElementPtr StatsMgr::statisticResetAllHandler(const std::string& /*name*/, const isc::data::ConstElementPtr& /*params*/) { - instance().resetAll(); + StatsMgr::instance().resetAll(); return (createAnswer(CONTROL_RESULT_SUCCESS, "All statistics reset to neutral values.")); } +isc::data::ConstElementPtr +StatsMgr::statisticSetMaxSampleAgeAllHandler(const std::string& /*name*/, + const isc::data::ConstElementPtr& params) { + std::string error; + StatsDuration duration; + if (!StatsMgr::getStatDuration(params, duration, error)) { + return (createAnswer(CONTROL_RESULT_ERROR, error)); + } + StatsMgr::instance().setMaxSampleAgeAll(duration); + return (createAnswer(CONTROL_RESULT_SUCCESS, + "All statistics duration limit are set.")); +} + +isc::data::ConstElementPtr +StatsMgr::statisticSetMaxSampleCountAllHandler(const std::string& /*name*/, + const isc::data::ConstElementPtr& params) { + std::string error; + uint32_t max_samples; + if (!StatsMgr::getStatMaxSamples(params, max_samples, error)) { + return (createAnswer(CONTROL_RESULT_ERROR, error)); + } + StatsMgr::instance().setMaxSampleCountAll(max_samples); + return (createAnswer(CONTROL_RESULT_SUCCESS, + "All statistics count limit are set.")); +} + bool StatsMgr::getStatName(const isc::data::ConstElementPtr& params, std::string& name, @@ -254,5 +340,54 @@ StatsMgr::getStatName(const isc::data::ConstElementPtr& params, return (true); } +bool +StatsMgr::getStatDuration(const isc::data::ConstElementPtr& params, + StatsDuration& duration, + std::string& reason) { + if (!params) { + reason = "Missing mandatory 'duration' parameter."; + return (false); + } + ConstElementPtr stat_duration = params->get("duration"); + if (!stat_duration) { + reason = "Missing mandatory 'duration' parameter."; + return (false); + } + + int64_t time_duration = stat_duration->intValue(); + + int64_t hours = time_duration / 3600; + time_duration -= hours * 3600; + + int64_t minutes = time_duration / 60; + time_duration -= minutes * 60; + + int64_t seconds = time_duration; + duration = boost::posix_time::time_duration(hours, minutes, seconds, 0); + return (true); +} + +bool +StatsMgr::getStatMaxSamples(const isc::data::ConstElementPtr& params, + uint32_t& max_samples, + std::string& reason) { + if (!params) { + reason = "Missing mandatory 'max-samples' parameter."; + return (false); + } + ConstElementPtr stat_max_samples = params->get("max-samples"); + if (!stat_max_samples) { + reason = "Missing mandatory 'max-samples' parameter."; + return (false); + } + if (stat_max_samples->getType() != Element::integer) { + reason = "'max-samples' parameter expected to be an integer."; + return (false); + } + + max_samples = stat_max_samples->intValue(); + return (true); +} + }; }; diff --git a/src/lib/stats/stats_mgr.h b/src/lib/stats/stats_mgr.h index 085ab3cb5b60ebe5264c1cbc21ef330a24c4eca8..450d65a63bc15f2416dfffd84445e0064ea3a44a 100644 --- a/src/lib/stats/stats_mgr.h +++ b/src/lib/stats/stats_mgr.h @@ -149,7 +149,7 @@ class StatsMgr : public boost::noncopyable { /// /// Specifies that statistic name should be stored not as single value, but /// rather as a set of values. In this form, at most max_samples will be kept. - /// When adding max_samples+1 sample, the oldest sample will be discarded. + /// When adding max_samples + 1 sample, the oldest sample will be discarded. /// /// /// @param name name of the observation @@ -160,6 +160,12 @@ class StatsMgr : public boost::noncopyable { /// setMaxSampleCount("incoming-packets", 100); bool setMaxSampleCount(const std::string& name, uint32_t max_samples); + /// @brief Set duration limit for all collected statistics. + void setMaxSampleAgeAll(const StatsDuration& duration); + + /// @brief Set count limit for all collected statistics. + void setMaxSampleCountAll(uint32_t max_samples); + /// @} /// @defgroup consumer_methods Methods are used by data consumers. @@ -191,7 +197,7 @@ class StatsMgr : public boost::noncopyable { /// @brief Returns size of specified statistic. /// /// @param name name of the statistic which size should be return. - /// @return size of specified statistic. + /// @return size of specified statistic, 0 means lack of given statistic. size_t getSize(const std::string& name) const; /// @brief Returns number of available statistics. @@ -250,7 +256,7 @@ class StatsMgr : public boost::noncopyable { /// /// This method handles statistic-get command, which returns value /// of a given statistic). It expects one parameter stored in params map: - /// name: name-of-the-statistic + /// name: name of the statistic /// /// Example params structure: /// { @@ -268,7 +274,7 @@ class StatsMgr : public boost::noncopyable { /// /// This method handles statistic-reset command, which resets value /// of a given statistic. It expects one parameter stored in params map: - /// name: name-of-the-statistic + /// name: name of the statistic /// /// Example params structure: /// { @@ -286,7 +292,7 @@ class StatsMgr : public boost::noncopyable { /// /// This method handles statistic-reset command, which removes a given /// statistic completely. It expects one parameter stored in params map: - /// name: name-of-the-statistic + /// name: name of the statistic /// /// Example params structure: /// { @@ -300,6 +306,50 @@ class StatsMgr : public boost::noncopyable { statisticRemoveHandler(const std::string& name, const isc::data::ConstElementPtr& params); + /// @brief Handles statistic-sample-age-set command + /// + /// This method handles statistic-sample-age-set command, + /// which set max_sample_age_ limit of a given statistic + /// and leaves max_sample_count_ disabled. + /// It expects two parameters stored in params map: + /// name: name of the statistic + /// duration: time limit expressed as a number of seconds + /// + /// Example params structure: + /// { + /// "name": "packets-received", + /// "duration": 1245 + /// } + /// + /// @param name name of the command (ignored, should be "statistic-sample-age-set") + /// @param params structure containing a map that contains "name" and "duration" + /// @return answer containing information about successfully setup limit of statistic + static isc::data::ConstElementPtr + statisticSetMaxSampleAgeHandler(const std::string& name, + const isc::data::ConstElementPtr& params); + + /// @brief Handles statistic-sample-count-set command + /// + /// This method handles statistic-sample-count-set command, + /// which set max_sample_count_ limit of a given statistic + /// and leaves max_sample_age_ disabled. + /// It expects two parameters stored in params map: + /// name: name of the statistic + /// max-samples: count limit + /// + /// Example params structure: + /// { + /// "name": "packets-received", + /// "max-samples": 15 + /// } + /// + /// @param name name of the command (ignored, should be "statistic-sample-count-set") + /// @param params structure containing a map that contains "name" and "max-samples" + /// @return answer containing information about successfully setup limit of statistic + static isc::data::ConstElementPtr + statisticSetMaxSampleCountHandler(const std::string& name, + const isc::data::ConstElementPtr& params); + /// @brief Handles statistic-get-all command /// /// This method handles statistic-get-all command, which returns values @@ -336,6 +386,44 @@ class StatsMgr : public boost::noncopyable { statisticRemoveAllHandler(const std::string& name, const isc::data::ConstElementPtr& params); + /// @brief Handles statistic-sample-age-set-all command + /// + /// This method handles statistic-sample-age-set-all command, + /// which set max_sample_age_ limit to all statistics. + /// It expects one parameter stored in params map: + /// duration: limit expressed as a number of seconds + /// + /// Example params structure: + /// { + /// "duration": 1245 + /// } + /// + /// @param name name of the command (ignored, should be "statistic-sample-age-set-all") + /// @param params structure containing a map that contains "duration" + /// @return answer confirming success of this operation + static isc::data::ConstElementPtr + statisticSetMaxSampleAgeAllHandler(const std::string& name, + const isc::data::ConstElementPtr& params); + + /// @brief Handles statistic-sample-count-set-all command + /// + /// This method handles statistic-sample-count-set-all command, + /// which set max_sample_count_ limit of all statistics. + /// It expects one parameter stored in params map: + /// max-samples: count limit + /// + /// Example params structure: + /// { + /// "max-samples": 15 + /// } + /// + /// @param name name of the command (ignored, should be "statistic-sample-count-set-all") + /// @param params structure containing a map that contains "max-samples" + /// @return answer confirming success of this operation + static isc::data::ConstElementPtr + statisticSetMaxSampleCountAllHandler(const std::string& name, + const isc::data::ConstElementPtr& params); + /// @} private: @@ -433,6 +521,45 @@ private: std::string& name, std::string& reason); + /// @brief Utility method that attempts to extract duration limit for + /// a given statistic + /// + /// This method attempts to extract duration limit for a given statistic + /// from the params structure. + /// It is expected to be a map that contains four 'duration' elements: 'hours', + /// 'minutes', 'seconds' and 'milliseconds' + /// all are of type int. If present as expected, statistic duration + /// limit is set and true is returned. + /// If any of these four parameters is missing or is of incorrect type, + /// the reason is specified in reason parameter and false is returned. + /// + /// @param params parameters structure received in command + /// @param duration [out] duration limit for the statistic (if no error detected) + /// @param reason [out] failure reason (if error is detected) + /// @return true (if everything is ok), false otherwise + static bool getStatDuration(const isc::data::ConstElementPtr& params, + StatsDuration& duration, + std::string& reason); + + /// @brief Utility method that attempts to extract count limit for + /// a given statistic + /// + /// This method attempts to extract count limit for a given statistic + /// from the params structure. + /// It is expected to be a map that contains 'max-samples' element, + /// that is of type int. If present as expected, statistic count + /// limit (max_samples) is set and true is returned. + /// If missing or is of incorrect type, the reason is specified in reason + /// parameter and false is returned. + /// + /// @param params parameters structure received in command + /// @param max_samples [out] count limit for the statistic (if no error detected) + /// @param reason [out] failure reason (if error is detected) + /// @return true (if everything is ok), false otherwise + static bool getStatMaxSamples(const isc::data::ConstElementPtr& params, + uint32_t& max_samples, + std::string& reason); + // This is a global context. All statistics will initially be stored here. StatContextPtr global_; }; diff --git a/src/lib/stats/tests/observation_unittest.cc b/src/lib/stats/tests/observation_unittest.cc index 2cab680da8956dd16848779fd41513f3d25deb37..f241b19da3e55be7310d39ffd8ec2df48519d0e5 100644 --- a/src/lib/stats/tests/observation_unittest.cc +++ b/src/lib/stats/tests/observation_unittest.cc @@ -430,6 +430,52 @@ TEST_F(ObservationTest, setAgeLimit) { } } +// Test checks whether we can get max_sample_age_ and max_sample_count_ +// properly. +TEST_F(ObservationTest, getLimits) { + // First checks whether getting default values works properly + EXPECT_EQ(a.getMaxSampleAge().first, false); + EXPECT_EQ(b.getMaxSampleAge().first, false); + EXPECT_EQ(c.getMaxSampleAge().first, false); + EXPECT_EQ(d.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(a.getMaxSampleCount().second, 20); + EXPECT_EQ(b.getMaxSampleCount().second, 20); + EXPECT_EQ(c.getMaxSampleCount().second, 20); + EXPECT_EQ(d.getMaxSampleCount().second, 20); + + // change limit to time duration + ASSERT_NO_THROW(a.setMaxSampleAge(millisec::time_duration(0, 4, 5, 3))); + ASSERT_NO_THROW(b.setMaxSampleAge(millisec::time_duration(0, 4, 5, 3))); + ASSERT_NO_THROW(c.setMaxSampleAge(millisec::time_duration(0, 4, 5, 3))); + ASSERT_NO_THROW(d.setMaxSampleAge(millisec::time_duration(0, 4, 5, 3))); + + 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(a.getMaxSampleAge().second, millisec::time_duration(0, 4, 5, 3)); + EXPECT_EQ(b.getMaxSampleAge().second, millisec::time_duration(0, 4, 5, 3)); + EXPECT_EQ(c.getMaxSampleAge().second, millisec::time_duration(0, 4, 5, 3)); + EXPECT_EQ(d.getMaxSampleAge().second, millisec::time_duration(0, 4, 5, 3)); + + 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(a.getMaxSampleCount().second, 20); + EXPECT_EQ(b.getMaxSampleCount().second, 20); + EXPECT_EQ(c.getMaxSampleCount().second, 20); + EXPECT_EQ(d.getMaxSampleCount().second, 20); +} + // Test checks whether timing is reported properly. TEST_F(ObservationTest, timers) { ptime before = microsec_clock::local_time(); diff --git a/src/lib/stats/tests/stats_mgr_unittest.cc b/src/lib/stats/tests/stats_mgr_unittest.cc index c61636f70bc7d58e4ec83da845316d73478103bf..bf2114f5439a19715f81c34a52ade1cb709738e1 100644 --- a/src/lib/stats/tests/stats_mgr_unittest.cc +++ b/src/lib/stats/tests/stats_mgr_unittest.cc @@ -136,7 +136,7 @@ TEST_F(StatsMgrTest, getSize) { EXPECT_EQ(StatsMgr::instance().getSize("delta"), 1); } -// Test checks whether setting age limit and count limit works properly +// Test checks whether setting age limit and count limit works properly. TEST_F(StatsMgrTest, setLimits) { // Initializing of an integer type observation StatsMgr::instance().setValue("foo", static_cast(1)); @@ -161,6 +161,60 @@ TEST_F(StatsMgrTest, setLimits) { EXPECT_EQ(StatsMgr::instance().getSize("foo"), 100); } +// Test checks whether setting age limit and count limit to existing +// statistics works properly. +TEST_F(StatsMgrTest, setLimitsAll) { + // Set a couple of statistics + StatsMgr::instance().setValue("alpha", static_cast(1234)); + StatsMgr::instance().setValue("beta", 12.34); + StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4)); + StatsMgr::instance().setValue("delta", "Lorem ipsum"); + + // check the setting of time limit to existing statistics + EXPECT_NO_THROW(StatsMgr::instance().setMaxSampleAgeAll(time_duration(0, 0, 1, 0))); + + // check if time limit was set properly and whether count limit is disabled + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().second, + time_duration(0, 0, 1, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleAge().second, + time_duration(0, 0, 1, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleCount().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleAge().second, + time_duration(0, 0, 1, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleCount().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleAge().second, + time_duration(0, 0, 1, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleCount().first, false); + + // check the setting of count limit to existing statistics + EXPECT_NO_THROW(StatsMgr::instance().setMaxSampleCountAll(1200)); + + // check if count limit was set properly and whether count limit is disabled + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().second, 1200); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleCount().second, 1200); + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleAge().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleCount().second, 1200); + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleAge().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleCount().second, 1200); + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleAge().first, false); +} + // This test checks whether a single (get("foo")) and all (getAll()) // statistics are reported properly. TEST_F(StatsMgrTest, getGetAll) { @@ -718,4 +772,187 @@ TEST_F(StatsMgrTest, commandRemoveAll) { EXPECT_EQ(0, StatsMgr::instance().count()); } +// This test checks whether statistic-sample-age-set command really set +// max_sample_age_ limit correctly. +TEST_F(StatsMgrTest, commandSetMaxSampleAge) { + StatsMgr::instance().setValue("alpha", static_cast(1234)); + + ElementPtr params = Element::createMap(); + params->set("name", Element::create("alpha")); + params->set("duration", Element::create(1245)); // time_duration(0, 20, 45, 0) + + ConstElementPtr rsp = + StatsMgr::instance().statisticSetMaxSampleAgeHandler("statistic-sample-age-set", params); + int status_code; + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(CONTROL_RESULT_SUCCESS, status_code); + + // check if time limit was set properly and whether count limit is disabled + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().second, + time_duration(0, 20, 45, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().first, false); +} + +// Test checks if statistic-sample-age-set is able to handle: +// - a request without parameters +// - a request without duration parameter +// - a request with missing statistic name +// - a request for non-existing statistic. +TEST_F(StatsMgrTest, commandSetMaxSampleAgeNegative) { + // Case 1: a request without parameters + ConstElementPtr rsp = + StatsMgr::instance().statisticSetMaxSampleAgeHandler("statistic-sample-age-set", ElementPtr()); + int status_code; + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(status_code, CONTROL_RESULT_ERROR); + + // Case 2: a request without duration parameter + ElementPtr params = Element::createMap(); + params->set("name", Element::create("alpha")); + rsp = StatsMgr::instance().statisticSetMaxSampleAgeHandler("statistic-sample-age-set", params); + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(status_code, CONTROL_RESULT_ERROR); + + // Case 3: a request with missing statistic name + params = Element::createMap(); + params->set("duration", Element::create(100)); + rsp = StatsMgr::instance().statisticSetMaxSampleAgeHandler("statistic-sample-age-set", params); + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(status_code, CONTROL_RESULT_ERROR); + + // Case 4: a request for non-existing statistic + params->set("name", Element::create("alpha")); + rsp = StatsMgr::instance().statisticSetMaxSampleAgeHandler("statistic-sample-age-set", params); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'alpha' statistic found\" }", + rsp->str()); +} + +TEST_F(StatsMgrTest, commandSetMaxSampleAgeAll) { + // Set a couple of statistics + StatsMgr::instance().setValue("alpha", static_cast(1234)); + StatsMgr::instance().setValue("beta", 12.34); + StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4)); + StatsMgr::instance().setValue("delta", "Lorem ipsum"); + + ElementPtr params = Element::createMap(); + params->set("duration", Element::create(3765)); // time_duration(1, 2, 45, 0) + + ConstElementPtr rsp = + StatsMgr::instance().statisticSetMaxSampleAgeAllHandler("statistic-sample-age-set-all", params); + int status_code; + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(CONTROL_RESULT_SUCCESS, status_code); + + // check if time limit was set properly and whether count limit is disabled + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().second, + time_duration(1, 2, 45, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleAge().second, + time_duration(1, 2, 45, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleCount().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleAge().second, + time_duration(1, 2, 45, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleCount().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleAge().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleAge().second, + time_duration(1, 2, 45, 0)); + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleCount().first, false); +} + +// This test checks whether statistic-sample-count-set command really set +// max_sample_count_ limit correctly. +TEST_F(StatsMgrTest, commandSetMaxSampleCount) { + StatsMgr::instance().setValue("alpha", static_cast(1234)); + + ElementPtr params = Element::createMap(); + params->set("name", Element::create("alpha")); + params->set("max-samples", Element::create(15)); + + ConstElementPtr rsp = + StatsMgr::instance().statisticSetMaxSampleCountHandler("statistic-sample-count-set", params); + int status_code; + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(CONTROL_RESULT_SUCCESS, status_code); + + // check if time limit was set properly and whether duration limit is disabled + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().second, 15); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().first, false); +} + +// Test checks if statistic-sample-count-set is able to handle: +// - a request without parameters +// - a request without max-samples parameter +// - a request with missing statistic name +// - a request for non-existing statistic. +TEST_F(StatsMgrTest, commandSetMaxSampleCountNegative) { + // Case 1: a request without parameters + ConstElementPtr rsp = + StatsMgr::instance().statisticSetMaxSampleCountHandler("statistic-sample-count-set", ElementPtr()); + int status_code; + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(status_code, CONTROL_RESULT_ERROR); + + // Case 2: a request without max-samples parameter + ElementPtr params = Element::createMap(); + params->set("name", Element::create("alpha")); + rsp = StatsMgr::instance().statisticSetMaxSampleCountHandler("statistic-sample-count-set", params); + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(status_code, CONTROL_RESULT_ERROR); + + // Case 3: a request with missing statistic name + params = Element::createMap(); + params->set("max-samples", Element::create(10)); + rsp = StatsMgr::instance().statisticSetMaxSampleCountHandler("statistic-sample-count-set", params); + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(status_code, CONTROL_RESULT_ERROR); + + // Case 4: a request for non-existing statistic + params->set("name", Element::create("alpha")); + rsp = StatsMgr::instance().statisticSetMaxSampleCountHandler("statistic-sample-count-set", params); + EXPECT_EQ("{ \"result\": 1, \"text\": \"No 'alpha' statistic found\" }", + rsp->str()); +} + +TEST_F(StatsMgrTest, commandSetMaxSampleCountAll) { + // Set a couple of statistics + StatsMgr::instance().setValue("alpha", static_cast(1234)); + StatsMgr::instance().setValue("beta", 12.34); + StatsMgr::instance().setValue("gamma", time_duration(1, 2, 3, 4)); + StatsMgr::instance().setValue("delta", "Lorem ipsum"); + + ElementPtr params = Element::createMap(); + params->set("max-samples", Element::create(200)); + + ConstElementPtr rsp = + StatsMgr::instance().statisticSetMaxSampleCountAllHandler("statistic-sample-count-set-all", params); + int status_code; + ASSERT_NO_THROW(parseAnswer(status_code, rsp)); + EXPECT_EQ(CONTROL_RESULT_SUCCESS, status_code); + + // check if count limit was set properly and whether count limit is disabled + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleCount().second, 200); + EXPECT_EQ(StatsMgr::instance().getObservation("alpha")->getMaxSampleAge().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleCount().second, 200); + EXPECT_EQ(StatsMgr::instance().getObservation("beta")->getMaxSampleAge().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleCount().second, 200); + EXPECT_EQ(StatsMgr::instance().getObservation("gamma")->getMaxSampleAge().first, false); + + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleCount().first, true); + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleCount().second, 200); + EXPECT_EQ(StatsMgr::instance().getObservation("delta")->getMaxSampleAge().first, false); +} + };