+1956. [bug] tmark
+ Modified stat_cmds hook library to omit statisics
+ for non-existent subnets from results returned by
+ stat-lease4-get and stat-lease6-get commands.
+ (Gitlab #2033)
+
1955. [bug] tmark
kea-dhcp4 no longer sends DHCPNAKs in response to
DHCPREQUESTs for addresses for which it has no knowledge.
using namespace isc::hooks;
using namespace isc::stats;
using namespace isc::util;
+using namespace isc::log;
using namespace std;
namespace isc {
bool query_eof = !(query->getNextRow(query_row));
// Now we iterate over the selected range, building rows accordingly.
+ bool orphaned_stats = false;
for (auto cur_subnet = lower; cur_subnet != upper; ++cur_subnet) {
SubnetID cur_id = (*cur_subnet)->getID();
- // Add total only rows for subnets that occur before,
- // in-between, or after the subnets in the query content
- if ((cur_id < query_row.subnet_id_) ||
- (cur_id > query_row.subnet_id_) ||
- (query_eof)) {
+ // Skip any unexpected result set rows. These occur when
+ // subnets no longer exist but either their leases (memfile)
+ // or their leaseX-stat rows (db lease backends) still do.
+ SubnetID logged_id = 0;
+ while ((cur_id > query_row.subnet_id_) && (!query_eof)) {
+ orphaned_stats = true;
+ query_eof = !(query->getNextRow(query_row));
+ }
+
+ // Add total only rows for subnets that occur before
+ // or after the subnets in the query content. These are
+ // subnets which exist but for which there is not yet any
+ // lease data.
+ if ((cur_id < query_row.subnet_id_) || (query_eof)) {
// Generate a totals only row
addValueRow4(value_rows, cur_id, 0, 0);
continue;
query_eof = !(query->getNextRow(query_row));
}
-
// Add the row for the current subnet
if (add_row) {
addValueRow4(value_rows, cur_id, assigned, declined);
}
}
+ // If there are any orphaned statistics log it.
+ if (!(query_eof) || orphaned_stats) {
+ LOG_DEBUG(stat_cmds_logger, DBGLVL_TRACE_BASIC, STAT_CMDS_LEASE4_ORPHANED_STATS);
+ }
+
return (value_rows->size());
}
}
// Get the first query row
+ bool orphaned_stats = false;
LeaseStatsRow query_row;
bool query_eof = !(query->getNextRow(query_row));
-
for (auto cur_subnet = lower; cur_subnet != upper; ++cur_subnet) {
SubnetID cur_id = (*cur_subnet)->getID();
- // Add total only rows for subnets that occur before,
- // in-between, or after subnets in the query content
- if ((cur_id < query_row.subnet_id_) ||
- (cur_id > query_row.subnet_id_) ||
- (query_eof)) {
+ // Skip any unexpected result set rows. These occur when
+ // subnets no longer exist but either their leases (memfile)
+ // or their leaseX-stat rows (db lease backends) still do.
+ while ((cur_id > query_row.subnet_id_) && (!query_eof)) {
+ orphaned_stats = true;
+ query_eof = !(query->getNextRow(query_row));
+ }
+
+ // Add total only rows for subnets that occur before
+ // or after the subnets in the query content. These are
+ // subnets which exist but for which there is not yet any
+ // lease data.
+ if ((cur_id < query_row.subnet_id_) || (query_eof)) {
// Generate a totals only row
addValueRow6(value_rows, cur_id, 0, 0, 0);
continue;
}
}
+ // If there are any orphaned statistics log it.
+ if (!(query_eof) || orphaned_stats) {
+ LOG_DEBUG(stat_cmds_logger, DBGLVL_TRACE_BASIC, STAT_CMDS_LEASE6_ORPHANED_STATS);
+ }
+
return (value_rows->size());
}
extern const isc::log::MessageID STAT_CMDS_LEASE4_GET_FAILED = "STAT_CMDS_LEASE4_GET_FAILED";
extern const isc::log::MessageID STAT_CMDS_LEASE4_GET_INVALID = "STAT_CMDS_LEASE4_GET_INVALID";
extern const isc::log::MessageID STAT_CMDS_LEASE4_GET_NO_SUBNETS = "STAT_CMDS_LEASE4_GET_NO_SUBNETS";
+extern const isc::log::MessageID STAT_CMDS_LEASE4_ORPHANED_STATS = "STAT_CMDS_LEASE4_ORPHANED_STATS";
extern const isc::log::MessageID STAT_CMDS_LEASE6_FAILED = "STAT_CMDS_LEASE6_FAILED";
extern const isc::log::MessageID STAT_CMDS_LEASE6_GET = "STAT_CMDS_LEASE6_GET";
extern const isc::log::MessageID STAT_CMDS_LEASE6_GET_FAILED = "STAT_CMDS_LEASE6_GET_FAILED";
extern const isc::log::MessageID STAT_CMDS_LEASE6_GET_INVALID = "STAT_CMDS_LEASE6_GET_INVALID";
extern const isc::log::MessageID STAT_CMDS_LEASE6_GET_NO_SUBNETS = "STAT_CMDS_LEASE6_GET_NO_SUBNETS";
+extern const isc::log::MessageID STAT_CMDS_LEASE6_ORPHANED_STATS = "STAT_CMDS_LEASE6_ORPHANED_STATS";
namespace {
"STAT_CMDS_LEASE4_GET_FAILED", "stat-lease4-get command failed: parameters: %1, reason: %2",
"STAT_CMDS_LEASE4_GET_INVALID", "stat-lease4-get command is malformed or invalid, reason: %1",
"STAT_CMDS_LEASE4_GET_NO_SUBNETS", "stat-lease4-get, parameters: %1, %2\"",
+ "STAT_CMDS_LEASE4_ORPHANED_STATS", "stat-lease4-get command omitted statistics for one or more non-existent subnets",
"STAT_CMDS_LEASE6_FAILED", "stat-lease6-get command failed: reason: %1",
"STAT_CMDS_LEASE6_GET", "stat-lease6-get command successful, parameters: %1 rows found: %2",
"STAT_CMDS_LEASE6_GET_FAILED", "stat-lease6-get command failed: parameters: %1, reason: %2",
"STAT_CMDS_LEASE6_GET_INVALID", "stat-lease6-get command is malformed or invalid, reason: %1",
"STAT_CMDS_LEASE6_GET_NO_SUBNETS", "stat-lease6-get, parameters: %1, %2\"",
+ "STAT_CMDS_LEASE6_ORPHANED_STATS", "stat-lease6-get command omitted statistics for one or more non-existent subnets",
NULL
};
extern const isc::log::MessageID STAT_CMDS_LEASE4_GET_FAILED;
extern const isc::log::MessageID STAT_CMDS_LEASE4_GET_INVALID;
extern const isc::log::MessageID STAT_CMDS_LEASE4_GET_NO_SUBNETS;
+extern const isc::log::MessageID STAT_CMDS_LEASE4_ORPHANED_STATS;
extern const isc::log::MessageID STAT_CMDS_LEASE6_FAILED;
extern const isc::log::MessageID STAT_CMDS_LEASE6_GET;
extern const isc::log::MessageID STAT_CMDS_LEASE6_GET_FAILED;
extern const isc::log::MessageID STAT_CMDS_LEASE6_GET_INVALID;
extern const isc::log::MessageID STAT_CMDS_LEASE6_GET_NO_SUBNETS;
+extern const isc::log::MessageID STAT_CMDS_LEASE6_ORPHANED_STATS;
#endif // STAT_CMDS_MESSAGES_H
known subnets. The parameters supplied along with an explanation should
be logged.
+% STAT_CMDS_LEASE4_ORPHANED_STATS stat-lease4-get command omitted statistics for one or more non-existent subnets
+During processing the stat-lease4-get found statistics for subnet IDs which
+for non-existent subnets. These values were omitted from the command response
+returned to the user. This may occur when subnets have been removed from
+the configuration in a manner that did not also remove the statistics. While
+the existence of such statistics is not harmful, steps should be considered
+to remove them. For memfile lease storage, the problem should disappear
+upon configuration reload or server restart. For database lease storage the
+issue is more complicated and as of Kea 2.0.0 we do not yet have a clean
+solution.
+
% STAT_CMDS_LEASE6_FAILED stat-lease6-get command failed: reason: %1
The stat-lease6-get command has failed. The reason for failure is logged.
The parameters submitted with stat-lease6-get were valid but excluded all
known subnets. The parameters supplied along with an explanation should
be logged.
+
+% STAT_CMDS_LEASE6_ORPHANED_STATS stat-lease6-get command omitted statistics for one or more non-existent subnets
+During processing the stat-lease4-get found statistics for subnet IDs which
+for non-existent subnets. These values were omitted from the command response
+returned to the user. This may occur when subnets have been removed from
+the configuration in a manner that did not also remove the statistics. While
+the existence of such statistics is not harmful, steps should be considered
+to remove them. For memfile lease storage, the problem should disappear
+upon configuration reload or server restart. For database lease storage the
+issue is more complicated and as of Kea 2.0.0 we do not yet have a clean
+solution.
#include <cc/command_interpreter.h>
#include <cc/data.h>
#include <stats/stats_mgr.h>
+#include <testutils/gtest_utils.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <gtest/gtest.h>
}
}
+// Verifies that statistics for v4 subnets which no longer
+// exist are dropped from the result sets.
+TEST_F(StatCmdsTest, statLease4OrphanedStats) {
+
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr4();
+
+ // Now remove subnets 10,30, and 50 thereby orphaning their leases.
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ CfgSubnets4Ptr subnets = cfg_mgr.getCurrentCfg()->getCfgSubnets4();
+ ASSERT_NO_THROW_LOG(subnets->del(10));
+ ASSERT_NO_THROW_LOG(subnets->del(30));
+ ASSERT_NO_THROW_LOG(subnets->del(50));
+ cfg_mgr.commit();
+
+ // Note timestamp actual values are not important but are included
+ // for clarity.
+ std::vector<TestScenario> tests = {
+ {
+ "ALL-Subnets",
+ "{\n"
+ " \"command\": \"stat-lease4-get\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}",
+ "stat-lease4-get[all subnets]: 2 rows found",
+ "{\n"
+ "\"result-set\": {\n"
+ " \"columns\": [\n"
+ " \"subnet-id\", \"total-addresses\",\n"
+ " \"cumulative-assigned-addresses\",\n"
+ " \"assigned-addresses\", \"declined-addresses\"\n"
+ " ],\n"
+ " \"rows\": [\n"
+ " [ 20, 16, 0, 3, 0 ],\n"
+ " [ 40, 16, 0, 4, 0 ]\n"
+ " ],\n"
+ " \"timestamp\": \"2018-05-04 15:03:37.000000\" }\n"
+ "}\n"
+ }
+ };
+
+ for (auto test = tests.begin(); test != tests.end(); ++test) {
+ {
+ SCOPED_TRACE((*test).description_);
+ testCommand((*test).command_txt_, CONTROL_RESULT_SUCCESS,
+ (*test).exp_response_, (*test).exp_result_json);
+ }
+ }
+}
+
+// Verifies that statistics for v6 subnets which no longer
+// exist are dropped from the result sets.
+TEST_F(StatCmdsTest, statLease6OrphanedStats) {
+
+ // Initialize lease manager.
+ initLeaseMgr6();
+
+ // Now remove subnets 10,30, and 50 thereby orphaning their leases.
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ CfgSubnets6Ptr subnets = cfg_mgr.getCurrentCfg()->getCfgSubnets6();
+ ASSERT_NO_THROW_LOG(subnets->del(10));
+ ASSERT_NO_THROW_LOG(subnets->del(30));
+ ASSERT_NO_THROW_LOG(subnets->del(50));
+ cfg_mgr.commit();
+
+ // Note timestamp actual values are not important but are included
+ // for clarity.
+ std::vector<TestScenario> tests = {
+ {
+ "ALL-Subnets",
+ "{\n"
+ " \"command\": \"stat-lease6-get\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}",
+ "stat-lease6-get[all subnets]: 2 rows found",
+ "{\n"
+ "\"result-set\": {\n"
+ " \"columns\": [\n"
+ " \"subnet-id\", \"total-nas\",\n"
+ " \"cumulative-assigned-nas\", \"assigned-nas\",\n"
+ " \"declined-nas\", \"total-pds\",\n"
+ " \"cumulative-assigned-pds\", \"assigned-pds\"\n"
+ " ],\n"
+ " \"rows\": [\n"
+ " [ 20, 16777216, 0, 3, 0, 0, 0, 0 ],\n"
+ " [ 40, 16777216, 0, 0, 0, 0, 0, 0 ]\n"
+ " ],\n"
+ " \"timestamp\": \"2018-05-04 15:03:37.000000\" }\n"
+ "}\n"
+ }
+ };
+
+ for (auto test = tests.begin(); test != tests.end(); ++test) {
+ {
+ SCOPED_TRACE((*test).description_);
+ testCommand((*test).command_txt_, CONTROL_RESULT_SUCCESS,
+ (*test).exp_response_, (*test).exp_result_json);
+ }
+ }
+}
+
} // end of anonymous namespace