From ba1eba40072447d2a1233a370c14716ee8a78bc0 Mon Sep 17 00:00:00 2001 From: Francis Dupont Date: Tue, 8 Oct 2019 11:34:39 +0200 Subject: [PATCH] [596-add-a-hook-point-for-post-reconfiguration] Added cb[46]_updated hook points --- src/bin/dhcp4/dhcp4_hooks.dox | 14 ++- src/bin/dhcp6/dhcp6_hooks.dox | 14 ++- src/lib/database/audit_entry.h | 3 + src/lib/dhcpsrv/cb_ctl_dhcp4.cc | 44 ++++++++- src/lib/dhcpsrv/cb_ctl_dhcp6.cc | 44 ++++++++- src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc | 90 +++++++++++++++++++ 6 files changed, 205 insertions(+), 4 deletions(-) diff --git a/src/bin/dhcp4/dhcp4_hooks.dox b/src/bin/dhcp4/dhcp4_hooks.dox index a152b88f0a..53209b4dea 100644 --- a/src/bin/dhcp4/dhcp4_hooks.dox +++ b/src/bin/dhcp4/dhcp4_hooks.dox @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -66,6 +66,18 @@ to the end of this list. - Next step status: Status codes retured by the callouts installed on this hook point are ignored. + @subsection dhcpv4HooksCb4Update cb4_updated + - @b Arguments: + - name: audit_entries, type isc::db::AuditEntryCollectionPtr, direction: in + + - @b Description: this callout is executed when the server has completed + a configuration update using the Config Backend. The server provides + the audit entries as a never null pointer to a not empty collection + copied from the update apply method argument. + + - Next step status: Status codes retured by the callouts installed on + this hook point are ignored. + @subsection dhcpv4HooksBuffer4Receive buffer4_receive - @b Arguments: diff --git a/src/bin/dhcp6/dhcp6_hooks.dox b/src/bin/dhcp6/dhcp6_hooks.dox index 8b76fd5a04..d15551c99d 100644 --- a/src/bin/dhcp6/dhcp6_hooks.dox +++ b/src/bin/dhcp6/dhcp6_hooks.dox @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2019 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -66,6 +66,18 @@ to the end of this list. - Next step status: Status codes retured by the callouts installed on this hook point are ignored. + @subsection dhcpv6HooksCb6Update cb6_updated + - @b Arguments: + - name: audit_entries, type isc::db::AuditEntryCollectionPtr, direction: in + + - @b Description: this callout is executed when the server has completed + a configuration update using the Config Backend. The server provides + the audit entries as a never null pointer to a not empty collection + copied from the update apply method argument. + + - Next step status: Status codes retured by the callouts installed on + this hook point are ignored. + @subsection dhcpv6HooksBuffer6Receive buffer6_receive - @b Arguments: diff --git a/src/lib/database/audit_entry.h b/src/lib/database/audit_entry.h index bc5617ffe5..465705fcf6 100644 --- a/src/lib/database/audit_entry.h +++ b/src/lib/database/audit_entry.h @@ -241,6 +241,9 @@ typedef boost::multi_index_container< > > AuditEntryCollection; +//// @brief Pointer to the @c AuditEntryCollection object. +typedef boost::shared_ptr AuditEntryCollectionPtr; + } // end of namespace isc::db } // end of namespace isc diff --git a/src/lib/dhcpsrv/cb_ctl_dhcp4.cc b/src/lib/dhcpsrv/cb_ctl_dhcp4.cc index b08a9dd0ac..98dd526a6a 100644 --- a/src/lib/dhcpsrv/cb_ctl_dhcp4.cc +++ b/src/lib/dhcpsrv/cb_ctl_dhcp4.cc @@ -9,10 +9,33 @@ #include #include #include +#include +#include using namespace isc::db; using namespace isc::data; using namespace isc::process; +using namespace isc::hooks; + +namespace { + +/// Structure that holds registered hook indexes. +struct CbCtlHooks { + int hook_index_cb4_updated_; ///< index for "cb4_updated" hook point. + + /// Constructor that registers hook points for CBControlDHCPv4. + CbCtlHooks() { + hook_index_cb4_updated_ = HooksManager::registerHook("cb4_updated"); + } +}; + +// Declare a Hooks object. As this is outside any function or method, it +// will be instantiated (and the constructor run) when the module is loaded. +// As a result, the hook indexes will be defined before any method in this +// module is called. +CbCtlHooks hooks_; + +}; // anonymous namespace namespace isc { namespace dhcp { @@ -184,8 +207,27 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector, CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence()); } LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_CONFIG4_MERGED); -} + if (!audit_entries.empty() && + HooksManager::getHooksManager().calloutsPresent(hooks_.hook_index_cb4_updated_)) { + CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle(); + + // Use the RAII wrapper to make sure that the callout handle state is + // reset when this object goes out of scope. All hook points must do + // it to prevent possible circular dependency between the callout + // handle and its arguments. + ScopedCalloutHandleState callout_handle_state(callout_handle); + + // Pass a shared pointer to audit entries. + AuditEntryCollectionPtr ptr(new AuditEntryCollection(audit_entries)); + callout_handle->setArgument("audit_entries", ptr); + + // Call the callouts + HooksManager::callCallouts(hooks_.hook_index_cb4_updated_, *callout_handle); + + // Ignore the result. + } +} } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/cb_ctl_dhcp6.cc b/src/lib/dhcpsrv/cb_ctl_dhcp6.cc index 3e58da9c82..b43d5bcf27 100644 --- a/src/lib/dhcpsrv/cb_ctl_dhcp6.cc +++ b/src/lib/dhcpsrv/cb_ctl_dhcp6.cc @@ -9,10 +9,33 @@ #include #include #include +#include +#include using namespace isc::db; using namespace isc::data; using namespace isc::process; +using namespace isc::hooks; + +namespace { + +/// Structure that holds registered hook indexes. +struct CbCtlHooks { + int hook_index_cb6_updated_; ///< index for "cb6_updated" hook point. + + /// Constructor that registers hook points for CBControlDHCPv6. + CbCtlHooks() { + hook_index_cb6_updated_ = HooksManager::registerHook("cb6_updated"); + } +}; + +// Declare a Hooks object. As this is outside any function or method, it +// will be instantiated (and the constructor run) when the module is loaded. +// As a result, the hook indexes will be defined before any method in this +// module is called. +CbCtlHooks hooks_; + +}; // anonymous namespace namespace isc { namespace dhcp { @@ -183,8 +206,27 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence()); } LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_CONFIG6_MERGED); -} + if (!audit_entries.empty() && + HooksManager::getHooksManager().calloutsPresent(hooks_.hook_index_cb6_updated_)) { + CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle(); + + // Use the RAII wrapper to make sure that the callout handle state is + // reset when this object goes out of scope. All hook points must do + // it to prevent possible circular dependency between the callout + // handle and its arguments. + ScopedCalloutHandleState callout_handle_state(callout_handle); + + // Pass a shared pointer to audit entries. + AuditEntryCollectionPtr ptr(new AuditEntryCollection(audit_entries)); + callout_handle->setArgument("audit_entries", ptr); + + // Call the callouts + HooksManager::callCallouts(hooks_.hook_index_cb6_updated_, *callout_handle); + + // Ignore the result. + } +} } // end of namespace isc::dhcp } // end of namespace isc diff --git a/src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc b/src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc index 118fe1bd4d..e61c30cb11 100644 --- a/src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc +++ b/src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -26,6 +29,7 @@ using namespace isc::data; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::process; +using namespace isc::hooks; namespace { @@ -38,6 +42,8 @@ public: : timestamp_(), object_timestamp_(), audit_entries_() { CfgMgr::instance().clear(); initTimestamps(); + callback_name_ = std::string(""); + callback_audit_entries_.reset(); } /// @brief Destructor. @@ -45,6 +51,9 @@ public: // Unregister the factory to be tidy. ConfigBackendDHCPv4Mgr::instance().unregisterBackendFactory("memfile"); CfgMgr::instance().clear(); + // Unregister hooks. + HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("cb4_updated"); + HooksManager::preCalloutsLibraryHandle().deregisterAllCallouts("cb6_updated"); } /// @brief Creates new CREATE audit entry. @@ -160,6 +169,26 @@ public: return (false); } + /// @brief Callback that stores received callout name and received value. + /// + /// @param callout_handle Callout handle. + static int + cb4_updated_callout(CalloutHandle& callout_handle) { + callback_name_ = std::string("cb4_updated"); + callout_handle.getArgument("audit_entries", callback_audit_entries_); + return (0); + } + + /// @brief Callback that stores received callout name and received value. + /// + /// @param callout_handle Callout handle. + static int + cb6_updated_callout(CalloutHandle& callout_handle) { + callback_name_ = std::string("cb6_updated"); + callout_handle.getArgument("audit_entries", callback_audit_entries_); + return (0); + } + /// @brief Holds test timestamps. std::map timestamp_; @@ -168,8 +197,17 @@ public: /// @brief Collection of audit entries used in the unit tests. AuditEntryCollection audit_entries_; + + /// @brief Callback name. + static std::string callback_name_; + + /// @brief Callback value. + static AuditEntryCollectionPtr callback_audit_entries_; }; +std::string CBControlDHCPTest::callback_name_; +AuditEntryCollectionPtr CBControlDHCPTest::callback_audit_entries_; + // ************************ V4 tests ********************* /// @brief Naked @c CBControlDHCPv4 class exposing protected methods. @@ -764,6 +802,32 @@ TEST_F(CBControlDHCPv4Test, databaseConfigApplySubnetNotFetched) { testDatabaseConfigApply(getTimestamp(-3)); } +// This test verifies that the configuration updates calls the hook. +TEST_F(CBControlDHCPv4Test, databaseConfigApplyHook) { + + // Initialize Hooks Manager. + HooksManager::loadLibraries(HookLibsCollection()); + + // Install cb4_updated. + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "cb4_updated", cb4_updated_callout)); + + // Create audit entries. + addCreateAuditEntry("dhcp4_global_parameter"); + addCreateAuditEntry("dhcp4_option_def"); + addCreateAuditEntry("dhcp4_options"); + addCreateAuditEntry("dhcp4_shared_network"); + addCreateAuditEntry("dhcp4_subnet"); + + // Run the test. + testDatabaseConfigApply(getTimestamp(-5)); + + // Checks the callout. + EXPECT_EQ("cb4_updated", callback_name_); + ASSERT_TRUE(callback_audit_entries_); + EXPECT_TRUE(audit_entries_ == *callback_audit_entries_); +} + // ************************ V6 tests ********************* /// @brief Naked @c CBControlDHCPv6 class exposing protected methods. @@ -1362,4 +1426,30 @@ TEST_F(CBControlDHCPv6Test, databaseConfigApplySubnetNotFetched) { testDatabaseConfigApply(getTimestamp(-3)); } +// This test verifies that the configuration updates calls the hook. +TEST_F(CBControlDHCPv6Test, databaseConfigApplyHook) { + + // Initialize Hooks Manager. + HooksManager::loadLibraries(HookLibsCollection()); + + // Install cb6_updated. + EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout( + "cb6_updated", cb6_updated_callout)); + + // Create audit entries. + addCreateAuditEntry("dhcp6_global_parameter"); + addCreateAuditEntry("dhcp6_option_def"); + addCreateAuditEntry("dhcp6_options"); + addCreateAuditEntry("dhcp6_shared_network"); + addCreateAuditEntry("dhcp6_subnet"); + + // Run the test. + testDatabaseConfigApply(getTimestamp(-5)); + + // Checks the callout. + EXPECT_EQ("cb6_updated", callback_name_); + ASSERT_TRUE(callback_audit_entries_); + EXPECT_TRUE(audit_entries_ == *callback_audit_entries_); +} + } -- 2.47.2