From: Stephen Morris Date: Fri, 21 Jun 2013 19:14:41 +0000 (+0100) Subject: [2980] Checkpoint - HooksManager class partially working X-Git-Tag: bind10-1.2.0beta1-release~345^2~17 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=49ee4eaf84ad72a044cf4957af016d1e8e15cab0;p=thirdparty%2Fkea.git [2980] Checkpoint - HooksManager class partially working --- diff --git a/src/lib/hooks/Makefile.am b/src/lib/hooks/Makefile.am index 75b3daf4f5..08863be2d0 100644 --- a/src/lib/hooks/Makefile.am +++ b/src/lib/hooks/Makefile.am @@ -31,6 +31,7 @@ libb10_hooks_la_SOURCES += callout_handle.cc callout_handle.h libb10_hooks_la_SOURCES += callout_manager.cc callout_manager.h libb10_hooks_la_SOURCES += hooks.h libb10_hooks_la_SOURCES += hooks_log.cc hooks_log.h +libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h libb10_hooks_la_SOURCES += library_handle.cc library_handle.h libb10_hooks_la_SOURCES += library_manager.cc library_manager.h libb10_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h diff --git a/src/lib/hooks/callout_handle.h b/src/lib/hooks/callout_handle.h index 81cfb5f1ef..3792c589d0 100644 --- a/src/lib/hooks/callout_handle.h +++ b/src/lib/hooks/callout_handle.h @@ -356,7 +356,7 @@ private: /// A shared pointer to a CalloutHandle object. typedef boost::shared_ptr CalloutHandlePtr; -} // namespace util +} // namespace hooks } // namespace isc diff --git a/src/lib/hooks/hooks_manager.cc b/src/lib/hooks/hooks_manager.cc new file mode 100644 index 0000000000..0b304d6ee9 --- /dev/null +++ b/src/lib/hooks/hooks_manager.cc @@ -0,0 +1,109 @@ +// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// TODO - This is a temporary implementation of the hooks manager - it is +// likely to be completely rewritte + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using namespace std; + +namespace isc { +namespace hooks { + +// Constructor + +HooksManager::HooksManager() { +} + +// Return reference to singleton hooks manager. + +HooksManager& +HooksManager::getHooksManager() { + static HooksManager manager; + return (manager); +} + +// Perform conditional initialization if nothing is loaded. + +void +HooksManager::conditionallyInitialize() { + if (!lm_collection_) { + + // Nothing present, so create the collection with any empty set of + // libraries, and get the CalloutManager. + vector libraries; + lm_collection_.reset(new LibraryManagerCollection(libraries)); + lm_collection_->loadLibraries(); + + callout_manager_ = lm_collection_->getCalloutManager(); + } +} + +// Create a callout handle + +boost::shared_ptr +HooksManager::createCalloutHandleInternal() { + conditionallyInitialize(); + return (boost::shared_ptr( + new CalloutHandle(callout_manager_))); +} + +boost::shared_ptr +HooksManager::createCalloutHandle() { + return (getHooksManager().createCalloutHandleInternal()); +} + +// Are callouts present? + +bool +HooksManager::calloutsPresentInternal(int index) { + conditionallyInitialize(); + return (callout_manager_->calloutsPresent(index)); +} + +bool +HooksManager::calloutsPresent(int index) { + return (getHooksManager().calloutsPresentInternal(index)); +} + +// Call the callouts + +void +HooksManager::callCalloutsInternal(int index, CalloutHandle& handle) { + conditionallyInitialize(); + return (callout_manager_->callCallouts(index, handle)); +} + +void +HooksManager::callCallouts(int index, CalloutHandle& handle) { + return (getHooksManager().callCalloutsInternal(index, handle)); +} + + + + +} // namespace util +} // namespace isc diff --git a/src/lib/hooks/hooks_manager.h b/src/lib/hooks/hooks_manager.h new file mode 100644 index 0000000000..20483098e8 --- /dev/null +++ b/src/lib/hooks/hooks_manager.h @@ -0,0 +1,221 @@ +// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef HOOKS_MANAGER_H +#define HOOKS_MANAGER_H + +#include +#include + +#include +#include + +namespace isc { +namespace hooks { + +// Forward declarations +class CalloutHandle; +class CalloutManager; +class LibraryHandle; +class LibraryManagerCollection; + +/// @brief Hooks Manager +/// +/// This is the overall manager of the hooks framework and is the main class +/// used by a BIND 10 module when handling hooks. It is responsible for the +/// loading and unloading of user libraries, and for calling the callouts on +/// each hook point. +/// +/// The class is a singleton, the single instance of the object being accessed +/// through the static getHooksManager() method. + +class HooksManager : boost::noncopyable { +public: + /// @brief Get singleton hooks manager + /// + /// @return Reference to the singleton hooks manager. + static HooksManager& getHooksManager(); + + /// @brief Reset hooks manager + /// + /// Resets the hooks manager to the initial state. This should only be + /// called by test functions, so causes a warning message to be output. + void reset() {} + + /// @brief Load and reload libraries + /// + /// Loads the list of libraries into the server address space. For each + /// library, the "standard" functions (ones with the same names as the + /// hook points) are configured and the libraries' "load" function + /// called. + /// + /// If libraries are already loaded, they are unloaded and the new + /// libraries loaded. + /// + /// If any library fails to load, an error message will be logged. The + /// remaining libraries will be loaded if possible. + /// + /// @param libraries List of libraries to be loaded. The order is + /// important, as it determines the order that callouts on the same + /// hook will be called. + /// + /// @return true if all libraries loaded without a problem, false if one or + /// more libraries failed to load. In the latter case, message will + /// be logged that give the reason. + bool loadLibraries(const std::vector& /* libraries */) {return false;} + + /// @brief Unload libraries + /// + /// Unloads the loaded libraries and leaves the hooks subsystem in the + /// state it was after construction but before loadLibraries() is called. + /// + /// @note: This method should be used with caution - see the notes for + /// the class LibraryManager for pitfalls. In general, a server + /// should not call this method: library unloading will automatically + /// take place when new libraries are loaded, and when appropriate + /// objects are destroyed. + /// + /// @return true if all libraries unloaded successfully, false on an error. + /// In the latter case, an error message will have been output. + bool unloadLibraries() {return false;} + + /// @brief Are callouts present? + /// + /// Checks loaded libraries and returns true if at lease one callout + /// has been registered by them for the given hook. + /// + /// @param index Hooks index for which callouts are checked. + /// + /// @return true if callouts are present, false if not. + /// @throw NoSuchHook Given index does not correspond to a valid hook. + static bool calloutsPresent(int index); + + /// @brief Calls the callouts for a given hook + /// + /// Iterates through the libray handles and calls the callouts associated + /// with the given hook index. + /// + /// @note This method invalidates the current library index set with + /// setLibraryIndex(). + /// + /// @param index Index of the hook to call. + /// @param handle Reference to the CalloutHandle object for the current + /// object being processed. + static void callCallouts(int index, CalloutHandle& handle); + + /// @brief Return pre-callouts library handle + /// + /// Returns a library handle that can be used by the server to register + /// callouts on a hook that are called _before_ any callouts belonging + /// to a library. + /// + /// @note This library handle is valid only after loadLibraries() is + /// called and before another call to loadLibraries(). Its use + /// at any other time may cause severe problems. + /// + /// TODO: This is also invalidated by a call to obtaining the + /// post-callout function. + /// + /// @return Shared pointer to library handle associated with pre-library + /// callout registration. + boost::shared_ptr preCalloutLibraryHandle() const; + + /// @brief Return post-callouts library handle + /// + /// Returns a library handle that can be used by the server to register + /// callouts on a hook that are called _after any callouts belonging + /// to a library. + /// + /// @note This library handle is valid only after loadLibraries() is + /// called and before another call to loadLibraries(). Its use + /// at any other time may cause severe problems. + /// + /// TODO: This is also invalidated by a call to obtaining the + /// pret-callout function. + /// + /// @return Shared pointer to library handle associated with post-library + /// callout registration. + boost::shared_ptr postCalloutLibraryHandle() const; + + /// @brief Return callout handle + /// + /// Returns a callout handle to be associated with a request passed round + /// the system. + /// + /// @note This handle is valid only after a loadLibraries() call and then + /// only up to the next loadLibraries() call. + /// + /// @return Shared pointer to a CalloutHandle object. + static boost::shared_ptr createCalloutHandle(); + +private: + + /// @brief Constructor + /// + /// This is private as the object is a singleton and can only be addessed + /// through the getHooksManager() static method. + HooksManager(); + + //@{ + /// The following correspond to the each of the static methods above + /// but operate on the current instance. + + /// @brief Are callouts present? + /// + /// @param index Hooks index for which callouts are checked. + /// + /// @return true if callouts are present, false if not. + /// @throw NoSuchHook Given index does not correspond to a valid hook. + bool calloutsPresentInternal(int index); + + /// @brief Calls the callouts for a given hook + /// + /// @param index Index of the hook to call. + /// @param handle Reference to the CalloutHandle object for the current + /// object being processed. + void callCalloutsInternal(int index, CalloutHandle& handle); + + /// @brief Return callout handle + /// + /// @note This handle is valid only after a loadLibraries() call and then + /// only up to the next loadLibraries() call. + /// + /// @return Shared pointer to a CalloutHandle object. + boost::shared_ptr createCalloutHandleInternal(); + + //@} + + /// @brief Conditional initialization of the hooks manager + /// + /// loadLibraries() performs the initialization of the HooksManager, + /// setting up the internal structures and loading libraries. However, + /// in some cases, server authors may not do that. This method is called + /// whenever any hooks execution function is invoked (checking callouts, + /// calling callouts or returning a callout handle). If the HooksManager + /// is unitialised, it will initialize it with an "empty set" of libraries. + void conditionallyInitialize(); + + // Members + + /// Set of library managers. + boost::shared_ptr lm_collection_; + + /// Callout manager for the set of library managers. + boost::shared_ptr callout_manager_; +}; + +} // namespace util +} // namespace hooks + +#endif // HOOKS_MANAGER_H diff --git a/src/lib/hooks/tests/Makefile.am b/src/lib/hooks/tests/Makefile.am index 05364aa695..ba23c3044b 100644 --- a/src/lib/hooks/tests/Makefile.am +++ b/src/lib/hooks/tests/Makefile.am @@ -73,10 +73,14 @@ run_unittests_SOURCES += callout_handle_unittest.cc run_unittests_SOURCES += callout_manager_unittest.cc run_unittests_SOURCES += common_test_class.h run_unittests_SOURCES += handles_unittest.cc +run_unittests_SOURCES += hooks_manager_unittest.cc run_unittests_SOURCES += library_manager_collection_unittest.cc run_unittests_SOURCES += library_manager_unittest.cc run_unittests_SOURCES += server_hooks_unittest.cc +nodist_run_unittests_SOURCES = marker_file.h +nodist_run_unittests_SOURCES += test_libraries.h + run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) run_unittests_LDADD = $(AM_LDADD) $(GTEST_LDADD) diff --git a/src/lib/hooks/tests/hooks_manager_unittest.cc b/src/lib/hooks/tests/hooks_manager_unittest.cc new file mode 100644 index 0000000000..0d827ae46b --- /dev/null +++ b/src/lib/hooks/tests/hooks_manager_unittest.cc @@ -0,0 +1,209 @@ +// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include +#include + +#include +#include + +#include +#include + + +using namespace isc; +using namespace isc::hooks; +using namespace std; + +namespace { + +/// @brief Hooks manager collection test class + +class HooksManagerTest : public ::testing::Test, + public HooksCommonTestClass { +public: + /// @brief Constructor + /// + /// Reset the hooks manager. The hooks manager is a singleton, so needs + /// to be reset for each test. + HooksManagerTest() { + HooksManager::getHooksManager().reset(); + } + + /// @brief Call callouts test + /// + /// See the header for HooksCommonTestClass::execute for details. + /// + /// @param r0...r3, d1..d3 Values and intermediate values expected. They + /// are ordered so that the variables appear in the argument list in + /// the order they are used. + void executeCallCallouts(int r0, int d1, int r1, int d2, int r2, int d3, + int r3) { + static const char* COMMON_TEXT = " callout returned the wong value"; + static const char* RESULT = "result"; + + // Get a CalloutHandle for the calculation. + CalloutHandlePtr handle = HooksManager::createCalloutHandle(); + + // Initialize the argument RESULT. This simplifies testing by + // eliminating the generation of an exception when we try the unload + // test. In that case, RESULT is unchanged. + int result = -1; + handle->setArgument(RESULT, result); + + // Seed the calculation. + HooksManager::callCallouts(isc::hooks::ServerHooks::CONTEXT_CREATE, + *handle); + handle->getArgument(RESULT, result); + EXPECT_EQ(r0, result) << "context_create" << COMMON_TEXT; + + // Perform the first calculation. + handle->setArgument("data_1", d1); + HooksManager::callCallouts(lm_one_index_, *handle); + handle->getArgument(RESULT, result); + EXPECT_EQ(r1, result) << "lm_one" << COMMON_TEXT; + + // ... the second ... + handle->setArgument("data_2", d2); + HooksManager::callCallouts(lm_two_index_, *handle); + handle->getArgument(RESULT, result); + EXPECT_EQ(r2, result) << "lm_two" << COMMON_TEXT; + + // ... and the third. + handle->setArgument("data_3", d3); + HooksManager::callCallouts(lm_three_index_, *handle); + handle->getArgument(RESULT, result); + EXPECT_EQ(r3, result) << "lm_three" << COMMON_TEXT; + } + +}; +/* +// This is effectively the same test as for LibraryManager, but using the +// LibraryManagerCollection object. + +TEST_F(HooksManagerTest, LoadLibraries) { + + // Set up the list of libraries to be loaded. + std::vector library_names; + library_names.push_back(std::string(FULL_CALLOUT_LIBRARY)); + library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY)); + + // Set up the library manager collection and get the callout manager we'll + // be using. + PublicLibraryManagerCollection lm_collection(library_names); + + // Load the libraries. + EXPECT_TRUE(lm_collection.loadLibraries()); + boost::shared_ptr manager = + lm_collection.getCalloutManager(); + + // Execute the callouts. The first library implements the calculation. + // + // r3 = (7 * d1 - d2) * d3 + // + // The last-loaded library implements the calculation + // + // r3 = (10 + d1) * d2 - d3 + // + // Putting the processing for each library together in the appropriate + // order, we get: + // + // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3 + { + SCOPED_TRACE("Doing calculation with libraries loaded"); + executeCallCallouts(manager, 10, 3, 33, 2, 62, 3, 183); + } + + // Try unloading the libraries. + EXPECT_NO_THROW(lm_collection.unloadLibraries()); + + // Re-execute the calculation - callouts can be called but as nothing + // happens, the result should always be -1. + { + SCOPED_TRACE("Doing calculation with libraries not loaded"); + executeCallCallouts(manager, -1, 3, -1, 22, -1, 83, -1); + } +} + +// This is effectively the same test as above, but with a library generating +// an error when loaded. It is expected that the failing library will not be +// loaded, but others will be. + +TEST_F(LibraryManagerCollectionTest, LoadLibrariesWithError) { + + // Set up the list of libraries to be loaded. + std::vector library_names; + library_names.push_back(std::string(FULL_CALLOUT_LIBRARY)); + library_names.push_back(std::string(INCORRECT_VERSION_LIBRARY)); + library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY)); + + // Set up the library manager collection and get the callout manager we'll + // be using. + PublicLibraryManagerCollection lm_collection(library_names); + + // Load the libraries. We expect a failure status to be returned as + // one of the libraries failed to load. + EXPECT_FALSE(lm_collection.loadLibraries()); + boost::shared_ptr manager = + lm_collection.getCalloutManager(); + + // Expect only two libraries were loaded. + EXPECT_EQ(2, manager->getNumLibraries()); + + // Execute the callouts. The first library implements the calculation. + // + // r3 = (7 * d1 - d2) * d3 + // + // The last-loaded library implements the calculation + // + // r3 = (10 + d1) * d2 - d3 + // + // Putting the processing for each library together in the appropriate + // order, we get: + // + // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3 + { + SCOPED_TRACE("Doing calculation with libraries loaded"); + executeCallCallouts(manager, 10, 3, 33, 2, 62, 3, 183); + } + + // Try unloading the libraries. + EXPECT_NO_THROW(lm_collection.unloadLibraries()); + + // Re-execute the calculation - callouts can be called but as nothing + // happens, the result should always be -1. + { + SCOPED_TRACE("Doing calculation with libraries not loaded"); + executeCallCallouts(manager, -1, 3, -1, 22, -1, 83, -1); + } +} +*/ +// Check that everything works even with no libraries loaded. First that +// calloutsPresent() always returns false. + +TEST_F(HooksManagerTest, NoLibrariesCalloutsPresent) { + // No callouts should be present on any hooks. + EXPECT_FALSE(HooksManager::calloutsPresent(lm_one_index_)); + EXPECT_FALSE(HooksManager::calloutsPresent(lm_two_index_)); + EXPECT_FALSE(HooksManager::calloutsPresent(lm_three_index_)); +} + +TEST_F(HooksManagerTest, NoLibrariesCallCallouts) { + executeCallCallouts(-1, 3, -1, 22, -1, 83, -1); +} + +} // Anonymous namespace diff --git a/src/lib/hooks/tests/library_manager_collection_unittest.cc b/src/lib/hooks/tests/library_manager_collection_unittest.cc index a9295d0217..762d85001e 100644 --- a/src/lib/hooks/tests/library_manager_collection_unittest.cc +++ b/src/lib/hooks/tests/library_manager_collection_unittest.cc @@ -16,10 +16,8 @@ #include #include #include -#include #include -#include #include #include