Browse Source

[2980] Checkpoint - HooksManager class partially working

Stephen Morris 12 years ago
parent
commit
49ee4eaf84

+ 1 - 0
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

+ 1 - 1
src/lib/hooks/callout_handle.h

@@ -356,7 +356,7 @@ private:
 /// A shared pointer to a CalloutHandle object.
 typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
 
-} // namespace util
+} // namespace hooks
 } // namespace isc
 
 

+ 109 - 0
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 <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/library_manager_collection.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+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<string> libraries;
+        lm_collection_.reset(new LibraryManagerCollection(libraries));
+        lm_collection_->loadLibraries();
+
+        callout_manager_ = lm_collection_->getCalloutManager();
+    }
+}
+
+// Create a callout handle
+
+boost::shared_ptr<CalloutHandle>
+HooksManager::createCalloutHandleInternal() {
+    conditionallyInitialize();
+    return (boost::shared_ptr<CalloutHandle>(
+                             new CalloutHandle(callout_manager_)));
+}
+
+boost::shared_ptr<CalloutHandle>
+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

+ 221 - 0
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 <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+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<std::string>& /* 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<LibraryHandle> 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<LibraryHandle> 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<CalloutHandle> 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<CalloutHandle> 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<LibraryManagerCollection> lm_collection_;
+
+    /// Callout manager for the set of library managers.
+    boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+} // namespace util
+} // namespace hooks
+
+#endif // HOOKS_MANAGER_H

+ 4 - 0
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)

+ 209 - 0
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 <hooks/callout_handle.h>
+#include <hooks/hooks_manager.h>
+
+#include <hooks/tests/common_test_class.h>
+#include <hooks/tests/test_libraries.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+
+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<std::string> 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<CalloutManager> 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<std::string> 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<CalloutManager> 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

+ 0 - 2
src/lib/hooks/tests/library_manager_collection_unittest.cc

@@ -16,10 +16,8 @@
 #include <hooks/callout_manager.h>
 #include <hooks/library_manager.h>
 #include <hooks/library_manager_collection.h>
-#include <hooks/server_hooks.h>
 
 #include <hooks/tests/common_test_class.h>
-#include <hooks/tests/marker_file.h>
 #include <hooks/tests/test_libraries.h>
 
 #include <boost/shared_ptr.hpp>