]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2033] stat cmds - omit stats for non-existent subnets
authorThomas Markwalder <tmark@isc.org>
Fri, 1 Oct 2021 18:39:33 +0000 (14:39 -0400)
committerThomas Markwalder <tmark@isc.org>
Fri, 22 Oct 2021 14:20:06 +0000 (10:20 -0400)
added ChangeLog entry

src/hooks/dhcp/stat_cmds/stat_cmds.cc
    LeaseStatCmdsImpl::makeResultSet4()
    LeaseStatCmdsImpl::makeResultSet6() - modified to omit statistics
    for non-existent subnets

src/hooks/dhcp/stat_cmds/stat_cmds_messages.*
    STAT_CMDS_LEASE4_ORPHANED_STATS
    STAT_CMDS_LEASE6_ORPHANED_STATS - new log messages

src/hooks/dhcp/stat_cmds/tests/stat_cmds_unittest.cc
    TEST_F(StatCmdsTest, statLease4OrphanedStats)
    TEST_F(StatCmdsTest, statLease6OrphanedStats) - new unit tests

ChangeLog
src/hooks/dhcp/stat_cmds/stat_cmds.cc
src/hooks/dhcp/stat_cmds/stat_cmds_messages.cc
src/hooks/dhcp/stat_cmds/stat_cmds_messages.h
src/hooks/dhcp/stat_cmds/stat_cmds_messages.mes
src/hooks/dhcp/stat_cmds/tests/stat_cmds_unittest.cc

index 0d2bd80d3abc0a7b32703fee8698381fa35a7918..a487be67dfd522cd760fbc05ec460e9104d2aa0c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+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.
index 4f7ab86bab0e2f1df101c044af87420e9f5bc3e3..68bbc89f69eb797b6287abb2254efad0eaba559a 100644 (file)
@@ -31,6 +31,7 @@ using namespace isc::asiolink;
 using namespace isc::hooks;
 using namespace isc::stats;
 using namespace isc::util;
+using namespace isc::log;
 using namespace std;
 
 namespace isc {
@@ -449,14 +450,24 @@ LeaseStatCmdsImpl::makeResultSet4(const ElementPtr& result_wrapper,
     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;
@@ -479,13 +490,17 @@ LeaseStatCmdsImpl::makeResultSet4(const ElementPtr& result_wrapper,
 
             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());
 }
 
@@ -559,17 +574,25 @@ LeaseStatCmdsImpl::makeResultSet6(const ElementPtr& result_wrapper,
     }
 
     // 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;
@@ -604,6 +627,11 @@ LeaseStatCmdsImpl::makeResultSet6(const ElementPtr& result_wrapper,
         }
     }
 
+    // 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());
 }
 
index 9c5d58b13f15b57dc611bb023687917d4cf7f05f..b43cbbb35a495903f0c483240d41a1330aefbce7 100644 (file)
@@ -13,11 +13,13 @@ extern const isc::log::MessageID STAT_CMDS_LEASE4_GET = "STAT_CMDS_LEASE4_GET";
 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 {
 
@@ -31,11 +33,13 @@ const char* values[] = {
     "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
 };
 
index 7baa360011120a8ac7695ac05186c7a4e3b0e3be..988ad43802d5290d08278fe01bcfca10634cab6a 100644 (file)
@@ -14,10 +14,12 @@ extern const isc::log::MessageID STAT_CMDS_LEASE4_GET;
 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
index d7be98c7ba53487bb04c1c744b91bd3170b3546e..519e8be246b08b6c7b04afa321027b9a20d4aa87 100644 (file)
@@ -38,6 +38,17 @@ The parameters submitted with stat-lease4-get were valid but excluded all
 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.
 
@@ -57,3 +68,14 @@ parameters.  A detailed explanation should be 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.
index 58e5fcefa3953e1dcd68d7b503897d97a1c8d94b..c857328c2b72e616d2443442586a33d738cfe1b5 100644 (file)
@@ -16,6 +16,7 @@
 #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>
@@ -1474,5 +1475,108 @@ TEST_F(StatCmdsTest, statLease6GetSubnetsNotFound) {
     }
 }
 
+// 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