]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3463] Added v6 implementation
authorThomas Markwalder <tmark@isc.org>
Wed, 5 Feb 2025 17:10:37 +0000 (12:10 -0500)
committerThomas Markwalder <tmark@isc.org>
Tue, 18 Feb 2025 18:54:19 +0000 (18:54 +0000)
/src/bin/dhcp6/dhcp6_srv.cc
    Dhcpv6Srv::processLocalizedQuery6() - add response6 to leases6_committed
    callout arguments

/src/bin/dhcp6/tests/hooks_unittest.cc
    Update tests to expect with response6

/src/hooks/dhcp/lease_cmds/lease_cmds.*
    Add leases6Committed() handler

/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc
    Add leases6_committed()callout

/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
    LEASE_CMDS_LEASES6_COMMITTED_FAILED - new message

/src/hooks/dhcp/lease_cmds/tests/lease_cmds6_unittest.cc
    TEST_F(Lease6CmdsTest, validLeases6Committed)
    TEST_F(Lease6CmdsTest, validLeases6CommittedMultiThreading)
    TEST_F(Lease6CmdsTest, nopLeases6Committed)
    TEST_F(Lease6CmdsTest, nopLeases6CommittedMultiThreading)
    - new tests

12 files changed:
src/bin/dhcp6/dhcp6_srv.cc
src/bin/dhcp6/tests/hooks_unittest.cc
src/hooks/dhcp/lease_cmds/lease_cmds.cc
src/hooks/dhcp/lease_cmds/lease_cmds.h
src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc
src/hooks/dhcp/lease_cmds/lease_cmds_messages.cc
src/hooks/dhcp/lease_cmds/lease_cmds_messages.h
src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
src/hooks/dhcp/lease_cmds/libloadtests/load_unload_unittests.cc
src/hooks/dhcp/lease_cmds/tests/lease_cmds4_unittest.cc
src/hooks/dhcp/lease_cmds/tests/lease_cmds6_unittest.cc
src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h

index 1c1cdac01373e7e31563c5838c15751d6714020d..263f58e2092105f207a94d6dcc8b522bf9785a36 100644 (file)
@@ -1246,6 +1246,10 @@ Dhcpv6Srv::processLocalizedQuery6(AllocEngine::ClientContext6& ctx) {
         // Also pass the corresponding query packet as argument
         callout_handle->setArgument("query6", query);
 
+        // Pass the response packet as an argument.
+        ScopedEnableOptionsCopy<Pkt6> rsp6_options_copy(rsp);
+        callout_handle->setArgument("response6", rsp);
+
         Lease6CollectionPtr new_leases(new Lease6Collection());
         if (!ctx.new_leases_.empty()) {
             // Filter out reused leases as they were not committed.
index 29b2d53e529790af0371455397f917fd6e7ab0ec..79b46370f890a39faae98cc3633c05e62cdd6f89 100644 (file)
@@ -2126,6 +2126,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRapidCommit) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -2200,6 +2201,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRapidCommitPrefixes) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -2304,6 +2306,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRequest) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -2482,6 +2485,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRequestPrefix) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -2661,6 +2665,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenew) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -2836,6 +2841,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRenewPrefix) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3012,6 +3018,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebind) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3190,6 +3197,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRebindPrefix) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3371,6 +3379,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedDecline) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3436,6 +3445,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedDeclineTwoNAs) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3504,6 +3514,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRelease) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3572,6 +3583,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleasePrefix) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3647,6 +3659,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedReleaseMultiple) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3709,6 +3722,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCache) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3802,6 +3816,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -3896,6 +3911,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
@@ -4005,6 +4021,7 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequestsPrefixes) {
     // Check if all expected parameters were really received
     vector<string> expected_argument_names;
     expected_argument_names.push_back("query6");
+    expected_argument_names.push_back("response6");
     expected_argument_names.push_back("deleted_leases6");
     expected_argument_names.push_back("leases6");
 
index 6d0173b7ff779991757345d12fb033220ca5e640..ce0b6d0dae61383e7e3c003c93079904435b266b 100644 (file)
@@ -498,11 +498,24 @@ public:
     /// @param handle Callout context - which is expected to contain the query4,
     /// response4, and leases4 arguments.
     /// @param mgr Pointer to the BindingVariableMgr singleton.
-    /// @throw Unexpected if there is no active lease or a processing error
-    /// occurs. LeaseCmdsConflict if the update fails because the lease
-    /// no longer exists in the back end.
+    /// @throw Unexpected if a processing error occurs. LeaseCmdsConflict if the
+    /// update fails because the lease no longer exists in the back end.
     static void leases4Committed(CalloutHandle& callout_handle,
                                  BindingVariableMgrPtr mgr);
+
+    /// @brief leases6_committed hookpoint handler.
+    ///
+    /// Evaluates the binding variables (if any), and updates the leases'
+    /// user-context accordingly.  This includes updating the leases in the lease
+    /// back end.
+    ///
+    /// @param handle Callout context - which is expected to contain the query6,
+    /// response6, and leases6 arguments.
+    /// @param mgr Pointer to the BindingVariableMgr singleton.
+    /// @throw Unexpected if there a processing error occurs. LeaseCmdsConflict
+    /// if the update fails because the lease no longer exists in the back end.
+    static void leases6Committed(CalloutHandle& callout_handle,
+                                 BindingVariableMgrPtr mgr);
 };
 
 void
@@ -2803,6 +2816,52 @@ LeaseCmdsImpl::leases4Committed(CalloutHandle& callout_handle,
     }
 }
 
+void
+LeaseCmdsImpl::leases6Committed(CalloutHandle& callout_handle,
+                                BindingVariableMgrPtr mgr) {
+    Pkt6Ptr query;
+    Pkt6Ptr response;
+    Lease6CollectionPtr leases;
+
+    // Get the necessary arguments.
+    callout_handle.getArgument("query6", query);
+    callout_handle.getArgument("response6", response);
+    callout_handle.getArgument("leases6", leases);
+
+    // In some cases we may have no active leases or no response.
+    if (leases->empty() || !response) {
+        return;
+    }
+
+    for (auto lease : *leases) {
+        try {
+            /// @todo - Users might want to only update NA or PD leases.
+            /// This could be done via adding a lease type to the variable.
+            /// V4 would not use it, for V6 it would restrict a variable
+            /// to only that type of lease.  If unspecified (default) do
+            /// both NA and PD leases.
+            // Only update a lease if its active.
+            if (lease->valid_lft_) {
+                if (mgr->evaluateVariables(query, response, lease)) {
+                    LeaseMgrFactory::instance().updateLease6(lease);
+                }
+            }
+        /// @todo - for now if any leases fail we stop.  This could lead
+        /// to inconsistencies in user-context content for leases belonging
+        /// the the same response. The lease6BulkApplyHandler() accumlates
+        /// failures but iterates over all the leases..
+        } catch (const NoSuchLease&) {
+            isc_throw(LeaseCmdsConflict, "failed to update"
+                      " the lease with address " << lease->addr_ <<
+                      " either because the lease has been"
+                      " deleted or it has changed in the database");
+        } catch (const std::exception& ex) {
+            isc_throw(Unexpected, "evaluating binding variables failed for: "
+                      << query->getLabel() << ", :" << ex.what());
+        }
+    }
+}
+
 int
 LeaseCmds::leaseAddHandler(CalloutHandle& handle) {
     return (impl_->leaseAddHandler(handle));
@@ -2911,5 +2970,11 @@ LeaseCmds::leases4Committed(CalloutHandle& callout_handle,
     impl_->leases4Committed(callout_handle, mgr);
 }
 
+void
+LeaseCmds::leases6Committed(CalloutHandle& callout_handle,
+                            BindingVariableMgrPtr mgr) {
+    impl_->leases6Committed(callout_handle, mgr);
+}
+
 } // end of namespace lease_cmds
 } // end of namespace isc
index 00758609304cf33ef769af3e5619dbd89d249894..9695a5d03666a2b8d4fb5b0c02dccebad84222f8 100644 (file)
@@ -622,7 +622,7 @@ public:
 
     /// @brief leases4_committed hookpoint handler.
     ///
-    /// Evaluates the binding variables (if any), and updates the given lease's
+    /// Evaluates the binding variables (if any), and updates the active lease's
     /// user-context accordingly.  This includes updating the lease in the lease
     /// back end.
     ///
@@ -633,6 +633,18 @@ public:
     leases4Committed(hooks::CalloutHandle& callout_handle,
                      BindingVariableMgrPtr mgr);
 
+    /// @brief leases6_committed hookpoint handler.
+    ///
+    /// Evaluates the binding variables (if any), and updates the active leases'
+    /// user-context accordingly.  This includes updating the leases in the lease
+    /// back end.
+    ///
+    /// @param handle Callout context - which is expected to contain the query6, response6,
+    /// and leases6 arguments.
+    /// @param mgr Pointer to the BindingVariableMgr singleton.
+    void
+    leases6Committed(hooks::CalloutHandle& callout_handle,
+                     BindingVariableMgrPtr mgr);
 private:
     /// Pointer to the actual implementation
     boost::shared_ptr<LeaseCmdsImpl> impl_;
index 2619ea5e07f641c1e82f55478efaec9bf12d0119..9744dc47b3f11a4bfd04ea55e9b2f5d57bf76f4d 100644 (file)
@@ -422,4 +422,27 @@ int leases4_committed(CalloutHandle& handle) {
     return (0);
 }
 
+/// @brief leases6_committed callout implementation.
+///
+/// @param handle callout handle.
+int leases6_committed(CalloutHandle& handle) {
+    CalloutHandle::CalloutNextStep status = handle.getStatus();
+    if (status == CalloutHandle::NEXT_STEP_DROP ||
+        status == CalloutHandle::NEXT_STEP_SKIP) {
+        return (0);
+    }
+
+    try {
+        LeaseCmds lease_cmds;
+        lease_cmds.leases6Committed(handle, binding_var_mgr);
+    } catch (const std::exception& ex) {
+        LOG_ERROR(lease_cmds_logger, LEASE_CMDS_LEASES6_COMMITTED_FAILED)
+            .arg(ex.what());
+        return (1);
+    }
+
+    return (0);
+}
+
+
 } // end extern "C"
index fb12339b26c725b198e6531d3729e771afc2052a..13ad0f3605c0b3ff6c021cdd98a54705b18cb0c0 100644 (file)
@@ -22,6 +22,7 @@ extern const isc::log::MessageID LEASE_CMDS_GET6_FAILED = "LEASE_CMDS_GET6_FAILE
 extern const isc::log::MessageID LEASE_CMDS_INIT_OK = "LEASE_CMDS_INIT_OK";
 extern const isc::log::MessageID LEASE_CMDS_LEASE4_OFFER_FAILED = "LEASE_CMDS_LEASE4_OFFER_FAILED";
 extern const isc::log::MessageID LEASE_CMDS_LEASES4_COMMITTED_FAILED = "LEASE_CMDS_LEASES4_COMMITTED_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_LEASES6_COMMITTED_FAILED = "LEASE_CMDS_LEASES6_COMMITTED_FAILED";
 extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4 = "LEASE_CMDS_RESEND_DDNS4";
 extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4_FAILED = "LEASE_CMDS_RESEND_DDNS4_FAILED";
 extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6 = "LEASE_CMDS_RESEND_DDNS6";
@@ -58,8 +59,9 @@ const char* values[] = {
     "LEASE_CMDS_GET4_FAILED", "lease4-get command failed (parameters: %1, reason: %2)",
     "LEASE_CMDS_GET6_FAILED", "lease6-get command failed (parameters: %1, reason: %2)",
     "LEASE_CMDS_INIT_OK", "loading Lease Commands hooks library successful",
-    "LEASE_CMDS_LEASE4_OFFER_FAILED", "processing error occurred evaluating binding variables for %1, %2",
-    "LEASE_CMDS_LEASES4_COMMITTED_FAILED", "processing error occurred evaluating binding variables for %1, %2",
+    "LEASE_CMDS_LEASE4_OFFER_FAILED", "processing error occurred evaluating binding variables:%1",
+    "LEASE_CMDS_LEASES4_COMMITTED_FAILED", "processing error occurred evaluating binding variables: %1",
+    "LEASE_CMDS_LEASES6_COMMITTED_FAILED", "processing error occurred evaluating binding variables: %1",
     "LEASE_CMDS_RESEND_DDNS4", "lease4-resend-ddns command successful: %1",
     "LEASE_CMDS_RESEND_DDNS4_FAILED", "lease4-resend-ddns command failed: %1",
     "LEASE_CMDS_RESEND_DDNS6", "lease6-resend-ddns command successful: %1",
index ee4589374820f33278842ac00703550f31bdc9b8..37218828e3a84201e36388692811be0213cd4622 100644 (file)
@@ -23,6 +23,7 @@ extern const isc::log::MessageID LEASE_CMDS_GET6_FAILED;
 extern const isc::log::MessageID LEASE_CMDS_INIT_OK;
 extern const isc::log::MessageID LEASE_CMDS_LEASE4_OFFER_FAILED;
 extern const isc::log::MessageID LEASE_CMDS_LEASES4_COMMITTED_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_LEASES6_COMMITTED_FAILED;
 extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4;
 extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4_FAILED;
 extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6;
index 28288a886b1c8ea2e0e3de3aeea227e581448ea1..4edc4c316a31278d6099b5afb551f1c6488effa9 100644 (file)
@@ -139,12 +139,14 @@ The lease6-wipe command is deprecated and it will be removed in the future.
 The lease6-wipe command has failed. Both the reason as well as the
 parameters passed are logged.
 
-% LEASE_CMDS_LEASE4_OFFER_FAILED processing error occurred evaluating binding variables for %1, %2
+% LEASE_CMDS_LEASE4_OFFER_FAILED processing error occurred evaluating binding variables:%1 
 This debug log is emitted when an error occurs in  the lease4_offer 
-handler is invoked. The arguments detail the query and the error. This
-error means binding-variable values were not added/updated for the lease.
+handler is invoked. The argument provides an explanation.
 
-% LEASE_CMDS_LEASES4_COMMITTED_FAILED processing error occurred evaluating binding variables for %1, %2
+% LEASE_CMDS_LEASES4_COMMITTED_FAILED processing error occurred evaluating binding variables: %1
 This debug log is emitted when an error occurs in  the leases4_committed 
-handler is invoked. The arguments detail the query and the error. This
-error means binding-variable values were not added/updated for the lease.
+handler is invoked. The argument provides an explanation.
+
+% LEASE_CMDS_LEASES6_COMMITTED_FAILED processing error occurred evaluating binding variables: %1
+This debug log is emitted when an error occurs in  the leases6_committed 
+handler is invoked. The argument provides an explanation.
index cc125d206b8db3fa3dc82a0a80ab945ff7a860a0..d86fa450610d3cfe1ac4fbc75a865b77bb860bc1 100644 (file)
@@ -57,9 +57,8 @@ public:
         ASSERT_NO_THROW_LOG(result = HooksManager::calloutsPresent(hook_index_lease4_offer_));
         EXPECT_EQ(result, (isc::dhcp::CfgMgr::instance().getFamily() == AF_INET));
 
-        /// @todo when v6 is ready, change to always expect true.
         ASSERT_NO_THROW_LOG(result = HooksManager::calloutsPresent(hook_index_leasesX_committed_));
-        EXPECT_EQ(result, (isc::dhcp::CfgMgr::instance().getFamily() == AF_INET));
+        EXPECT_EQ(result, true);
     }
 
     /// @brief Creates a set of configuration parameters valid for the library.
index fec61482f243ef9bec93277a4f992ca9bdad2c95..eb32463e799881ff857f16f974339156cfa00fc2 100644 (file)
@@ -3500,11 +3500,6 @@ void Lease4CmdsTest::testLease4Write() {
     testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
 }
 
-#define SCOPED_LINE(line) \
-    std::stringstream ss; \
-    ss << "Scenario at line: " << line; \
-    SCOPED_TRACE(ss.str());
-
 void
 Lease4CmdsTest::testValidLease4Offer() {
     // Initialize lease manager (false = v4, true = add leases)
index 8bdda0b78b37afaf223ecb18827eca9cc52e6707..f2388cdb53895ee86d29b30e7a29c08c5ec6defa 100644 (file)
 #include <dhcpsrv/resource_handler.h>
 #include <cc/command_interpreter.h>
 #include <cc/data.h>
+#include <lease_cmds.h>
 #include <lease_cmds_unittest.h>
 #include <stats/stats_mgr.h>
 #include <testutils/user_context_utils.h>
 #include <testutils/multi_threading_utils.h>
+#include <testutils/gtest_utils.h>
 
 #include <gtest/gtest.h>
 
@@ -36,6 +38,7 @@ using namespace isc::dhcp_ddns;
 using namespace isc::asiolink;
 using namespace isc::stats;
 using namespace isc::test;
+using namespace isc::lease_cmds;
 
 namespace {
 
@@ -477,6 +480,17 @@ public:
 
     /// @brief Check that lease6-write works as expected.
     void testLease6Write();
+
+    /// @brief Check that leases6_committed handler works as expected with
+    /// valid inputs.
+    void testValidLeases6Committed();
+
+    /// @brief Check that leases6_committed handler does not throw or alter
+    /// leases under NOP conditions:
+    /// 1. There is no repsonse packet
+    /// 2. There are no leases
+    /// 3. There are leases but none active
+    void testNopLeases6Committed();
 };
 
 void Lease6CmdsTest::testLease6AddMissingParams() {
@@ -4218,6 +4232,241 @@ void Lease6CmdsTest::testLease6Write() {
     testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
 }
 
+void
+Lease6CmdsTest::testValidLeases6Committed() {
+    // Initialize lease manager (false = v4, true = add leases)
+    initLeaseMgr(true, true);
+
+    struct Scenario {
+        uint32_t line_;
+        std::string config_;
+        std::string orig_context_;
+        std::string exp_context_;
+    };
+
+    std::list<Scenario> scenarios = {
+    {
+        // No variables configured, nothing in lease context.
+        __LINE__,
+        R"({})",
+        R"({})",
+        R"({})"
+    },
+    {
+        // lease context has no binding-variables, two configured
+        __LINE__,
+        R"^({"binding-variables":[
+            {
+                "name": "duid",
+                "expression": "hexstring(option[1].hex,':')",
+                "source": "query"
+            },
+            {
+                "name": "sub-id",
+                "expression": "hexstring(option[38].hex, ':')",
+                "source": "response"
+            }]})^",
+        R"({})",
+        R"({"ISC":{
+            "binding-variables":{
+                "duid": "01:02:03:04",
+                "sub-id": "05:06:07:08"
+            }
+        }})",
+    },
+    {
+        // lease context has binding-variables, none configured
+        // Current logic leaves lease untouched.
+        __LINE__,
+        R"({})",
+        R"({"ISC":{
+            "binding-variables":{
+                "duid": "01:02:03:04",
+                "sub-id": "05:06:07:08"
+            }
+        }})",
+        R"({"ISC":{
+            "binding-variables":{
+                "duid": "01:02:03:04",
+                "sub-id": "05:06:07:08"
+            }
+        }})",
+    },
+    {
+        // Evaluated variable value is an empty string.
+        __LINE__,
+        R"^({"binding-variables":[
+            {
+                "name": "my-var",
+                "expression": "''",
+                "source": "query"
+            }]})^",
+        R"({"ISC":{
+            "binding-variables":{
+                "my-var": "pre-existing value"
+            }
+        }})",
+        R"({"ISC":{
+            "binding-variables":{
+                "my-var": ""
+            }
+        }})",
+    }
+    };
+
+    // Create packet pair and lease.
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    // Add the client id.
+    OptionPtr client_id(new Option(Option::V6, D6O_CLIENTID,
+                                   { 0x01, 0x02, 0x03, 0x04 }));
+    query->addOption(client_id);
+
+    Pkt6Ptr response(new Pkt6(DHCPV6_REPLY, 7890));
+    OptionPtr subscriber_id(new Option(Option::V6, D6O_SUBSCRIBER_ID,
+                                       { 0x05, 0x06, 0x07, 0x08 }));
+    response->addOption(subscriber_id);
+
+    IOAddress na_addr("2001:db8:1::1");
+
+    // Iterater over scenarios.
+    for (auto const& scenario : scenarios) {
+        SCOPED_LINE(scenario.line_);
+
+        // Create and configure the manager.
+        BindingVariableMgrPtr mgr;
+        ASSERT_NO_THROW_LOG(mgr.reset(new BindingVariableMgr(AF_INET6)));
+        ConstElementPtr config;
+        ASSERT_NO_THROW_LOG(config = Element::fromJSON(scenario.config_));
+        ASSERT_NO_THROW_LOG(mgr->configure(config));
+
+        // Fetch the lease and set its user-context to the original content.
+        Lease6Ptr orig_lease = lmptr_->getLease6(Lease::TYPE_NA, na_addr);
+        ASSERT_TRUE(orig_lease);
+        ASSERT_TRUE(orig_lease->valid_lft_ > 0);
+
+        ConstElementPtr orig_context;
+        ASSERT_NO_THROW_LOG(orig_context = Element::fromJSON(scenario.orig_context_));
+        orig_lease->setContext(orig_context);
+        ASSERT_NO_THROW_LOG(lmptr_->updateLease6(orig_lease));
+
+        Lease6CollectionPtr leases(new Lease6Collection());
+        leases->push_back(orig_lease);
+
+        // Create a callout handle and add the expected arguments.
+        CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+        callout_handle->setArgument("query6", query);
+        callout_handle->setArgument("response6", response);
+        callout_handle->setArgument("leases6", leases);
+
+        // Invoke the leases4Committed handler.
+        LeaseCmds cmds;
+        ASSERT_NO_THROW_LOG(cmds.leases6Committed(*callout_handle, mgr));
+
+        // Fetch the lease.
+        Lease6Ptr after_lease = lmptr_->getLease6(Lease::TYPE_NA, na_addr);
+        ASSERT_TRUE(after_lease);
+
+        // Context contents should match the expected context content.
+        ConstElementPtr exp_context;
+        ASSERT_NO_THROW_LOG(exp_context = Element::fromJSON(scenario.exp_context_));
+        ASSERT_EQ(*(after_lease->getContext()), *exp_context);
+    }
+}
+
+void
+Lease6CmdsTest::testNopLeases6Committed() {
+    // Initialize lease manager (false = v4, true = add leases)
+    initLeaseMgr(true, true);
+
+    struct Scenario {
+        uint32_t line_;
+        DHCPv6MessageType response_type_;
+        bool send_lease_;
+        uint32_t valid_lft_;
+    };
+
+    // Configure a single variable.
+    std::string config =
+    R"^({"binding-variables":[
+    {
+        "name": "duid",
+        "expression": "hexstring(option[1].hex,':')",
+        "source": "query"
+    }]})^";
+
+    // Create and configure the manager.
+    BindingVariableMgrPtr mgr;
+    ASSERT_NO_THROW_LOG(mgr.reset(new BindingVariableMgr(AF_INET6)));
+    ASSERT_NO_THROW_LOG(mgr->configure(Element::fromJSON(config)));
+
+    // Scenarios should all result in no change to the lease.
+    std::list<Scenario> scenarios = {
+    {
+        // No leases.
+        __LINE__,
+        DHCPV6_REPLY,
+        false,
+        0
+    },
+    {
+        // No active leases.
+        __LINE__,
+        DHCPV6_REPLY,
+        true,
+        0
+    },
+    {
+        // No response.
+        __LINE__,
+        DHCPV6_NOTYPE,
+        true,
+        1000
+    }};
+
+    // Create query packet.
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    OptionPtr client_id(new Option(Option::V6, D6O_CLIENTID,
+                                   { 0x01, 0x02, 0x03, 0x04 }));
+    query->addOption(client_id);
+
+    IOAddress na_addr("2001:db8:1::1");
+    for (auto const& scenario : scenarios) {
+        SCOPED_LINE(scenario.line_);
+
+        Pkt6Ptr response;
+        if (scenario.response_type_ != DHCPV6_NOTYPE) {
+            response.reset(new Pkt6(scenario.response_type_, 1234));
+        }
+
+
+        // Fetch the lease and verify there is no context content.
+        Lease6Ptr orig_lease = lmptr_->getLease6(Lease::TYPE_NA, na_addr);
+        ASSERT_TRUE(orig_lease);
+        ASSERT_FALSE(orig_lease->getContext());
+        orig_lease->valid_lft_ =  scenario.valid_lft_;
+
+        Lease6CollectionPtr leases(new Lease6Collection());
+        if (scenario.send_lease_) {
+            leases->push_back(orig_lease);
+        }
+
+        // Create a callout handle and add the expected arguments.
+        CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
+        callout_handle->setArgument("query6", query);
+        callout_handle->setArgument("response6", response);
+        callout_handle->setArgument("leases6", leases);
+
+        // Invoke the leases4Committed handler.
+        LeaseCmds cmds;
+        ASSERT_NO_THROW_LOG(cmds.leases6Committed(*callout_handle, mgr));
+
+        // Fetch the lease. Context should still be empty.
+        Lease6Ptr after_lease = lmptr_->getLease6(Lease::TYPE_NA, na_addr);
+        ASSERT_TRUE(after_lease);
+        ASSERT_FALSE(after_lease->getContext());
+    }
+}
+
 TEST_F(Lease6CmdsTest, lease6AddMissingParams) {
     testLease6AddMissingParams();
 }
@@ -4999,4 +5248,22 @@ TEST_F(Lease6CmdsTest, lease6WriteMultiThreading) {
     testLease6Write();
 }
 
+TEST_F(Lease6CmdsTest, validLeases6Committed) {
+    testValidLeases6Committed();
+}
+
+TEST_F(Lease6CmdsTest, validLeases6CommittedMultiThreading) {
+    MultiThreadingTest mt(true);
+    testValidLeases6Committed();
+}
+
+TEST_F(Lease6CmdsTest, nopLeases6Committed) {
+    testNopLeases6Committed();
+}
+
+TEST_F(Lease6CmdsTest, nopLeases6CommittedMultiThreading) {
+    MultiThreadingTest mt(true);
+    testNopLeases6Committed();
+}
+
 } // end of anonymous namespace
index 0052c229c1feee5749e2f4b28f98f9b97ba6b49d..55635f55e0e2d67ac9e37c1082b91c05d6b32ebc 100644 (file)
 
 namespace {
 
+#define SCOPED_LINE(line) \
+    std::stringstream ss; \
+    ss << "Scenario at line: " << line; \
+    SCOPED_TRACE(ss.str());
+
 /// @brief High valid lifetime used for leases in the tests below.
 constexpr uint32_t HIGH_VALID_LIFETIME = 0xFFFFFFFE;