Browse Source

[2981] Added libreload functionality to DHCPv6 server

Stephen Morris 11 years ago
parent
commit
aac023f8e1

+ 1 - 1
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc

@@ -137,7 +137,7 @@ TEST_F(CtrlDhcpv4SrvTest, libreload) {
     ConstElementPtr result =
         ControlledDhcpv4Srv::execDhcpv4ServerCommand("libreload", params);
     ConstElementPtr comment = parseAnswer(rcode, result);
-    EXPECT_EQ(0, rcode); // expect success
+    EXPECT_EQ(0, rcode); // Expect success
 
     // Check that the libraries have unloaded and reloaded.  The libraries are
     // unloaded in the reverse order to which they are loaded.  When they load,

+ 17 - 1
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -26,16 +26,20 @@
 #include <dhcp6/dhcp6_log.h>
 #include <dhcp6/spec_config.h>
 #include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
 #include <util/buffer.h>
 
 #include <cassert>
 #include <iostream>
+#include <string>
+#include <vector>
 
 using namespace isc::asiolink;
 using namespace isc::cc;
 using namespace isc::config;
 using namespace isc::data;
 using namespace isc::dhcp;
+using namespace isc::hooks;
 using namespace isc::log;
 using namespace isc::util;
 using namespace std;
@@ -142,7 +146,19 @@ ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr
                                  "Shutting down.");
         return (answer);
     } else if (command == "libreload") {
-        // TODO - add library reloading
+        // TODO delete any stored CalloutHandles referring to the old libraries
+        // Get list of currently loaded libraries and reload them.
+        vector<string> loaded = HooksManager::getLibraryNames();
+        bool status = HooksManager::loadLibraries(loaded);
+        if (!status) {
+            LOG_ERROR(dhcp6_logger, DHCP6_RELOAD_FAIL);
+            ConstElementPtr answer = isc::config::createAnswer(1,
+                                     "Failed to reload hooks libraries.");
+            return (answer);
+        }
+        ConstElementPtr answer = isc::config::createAnswer(0,
+                                 "Hooks libraries successfully reloaded.");
+        return (answer);
     }
 
     ConstElementPtr answer = isc::config::createAnswer(1,

+ 5 - 0
src/bin/dhcp6/dhcp6_messages.mes

@@ -246,6 +246,11 @@ mandatory client-id option. This is most likely caused by a buggy client
 (or a relay that malformed forwarded message). This request will not be
 processed and a response with error status code will be sent back.
 
+% DHCP6_RELOAD_FAIL reload of hooks libraries failed
+A "libreload" command was issued to reload the hooks libraries but for
+some reason the reload failed.  Other error messages issued from the
+hooks framework will indicate the nature of the problem.
+
 % DHCP6_RENEW_UNKNOWN_SUBNET RENEW message received from client on unknown subnet (duid=%1, iaid=%2)
 A warning message indicating that a client is attempting to renew his lease,
 but the server does not have any information about the subnet this client belongs

+ 1 - 0
src/bin/dhcp6/tests/Makefile.am

@@ -61,6 +61,7 @@ dhcp6_unittests_SOURCES  = dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
+dhcp6_unittests_SOURCES += marker_file.cc
 dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
 dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc

+ 1 - 55
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -47,6 +47,7 @@ using namespace isc::asiolink;
 using namespace isc::config;
 using namespace isc::data;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using namespace isc::hooks;
 using namespace std;
 
@@ -323,61 +324,6 @@ public:
                             expected_data_len));
     }
 
-    /// @brief Check marker file
-    ///
-    /// Marker files are used by the load/unload functions in the hooks
-    /// libraries in these tests to signal whether they have been loaded or
-    /// unloaded.  The file (if present) contains a single line holding
-    /// a set of characters.
-    ///
-    /// This convenience function checks the file to see if the characters
-    /// are those expected.
-    ///
-    /// @param name Name of the marker file.
-    /// @param expected Characters expected.  If a marker file is present,
-    ///        it is expected to contain characters.  Therefore a value of NULL
-    ///        is used to signify that the marker file is not expected to be
-    ///        present.
-    ///
-    /// @return true if all tests pass, false if not (in which case a failure
-    ///         will have been logged).
-    bool
-    checkMarkerFile(const char* name, const char* expected) {
-        // Open the file for input
-        fstream file(name, fstream::in);
-
-        // Is it open?
-        if (!file.is_open()) {
-
-            // No.  This is OK if we don't expected is to be present but is
-            // a failure otherwise.
-            if (expected == NULL) {
-                return (true);
-            }
-            ADD_FAILURE() << "Unable to open " << name << ". It was expected "
-                          << "to be present and to contain the string '"
-                          << expected << "'";
-            return (false);
-        } else if (expected == NULL) {
-
-            // File is open but we don't expect it to be present.
-            ADD_FAILURE() << "Opened " << name << " but it is not expected to "
-                          << "be present.";
-            return (false);
-        }
-
-        // OK, is open, so read the data and see what we have.  Compare it
-        // against what is expected.
-        string content;
-        getline(file, content);
-
-        string expected_str(expected);
-        EXPECT_EQ(expected_str, content) << "Data was read from " << name;
-        file.close();
-
-        return (expected_str == content);
-    }
-
     int rcode_;          ///< Return code (see @ref isc::config::parseAnswer)
     Dhcpv6Srv srv_;      ///< Instance of the Dhcp6Srv used during tests
     ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)

+ 73 - 9
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

@@ -14,30 +14,37 @@
 
 #include <config.h>
 
+#include <config/ccsession.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
-#include <config/ccsession.h>
+#include <hooks/hooks_manager.h>
+
+#include "marker_file.h"
+#include "test_libraries.h"
 
 #include <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 
-#include <iostream>
 #include <fstream>
+#include <iostream>
 #include <sstream>
 
 #include <arpa/inet.h>
+#include <unistd.h>
 
 using namespace std;
 using namespace isc;
-using namespace isc::dhcp;
 using namespace isc::asiolink;
-using namespace isc::data;
 using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::hooks;
 
 namespace {
 
 class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv {
-    // "naked" DHCPv6 server, exposes internal fields
+    // "Naked" DHCPv6 server, exposes internal fields
 public:
     NakedControlledDhcpv6Srv():ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
 };
@@ -45,10 +52,25 @@ public:
 class CtrlDhcpv6SrvTest : public ::testing::Test {
 public:
     CtrlDhcpv6SrvTest() {
+        reset();
     }
 
     ~CtrlDhcpv6SrvTest() {
+        reset();
     };
+
+    /// @brief Reset hooks data
+    ///
+    /// Resets the data for the hooks-related portion of the test by ensuring
+    /// that no libraries are loaded and that any marker files are deleted.
+    void reset() {
+        // Unload any previously-loaded libraries.
+        HooksManager::unloadLibraries();
+
+        // Get rid of any marker files.
+        static_cast<void>(unlink(LOAD_MARKER_FILE));
+        static_cast<void>(unlink(UNLOAD_MARKER_FILE));
+    }
 };
 
 TEST_F(CtrlDhcpv6SrvTest, commands) {
@@ -62,12 +84,12 @@ TEST_F(CtrlDhcpv6SrvTest, commands) {
     ElementPtr params(new isc::data::MapElement());
     int rcode = -1;
 
-    // case 1: send bogus command
+    // Case 1: send bogus command
     ConstElementPtr result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("blah", params);
     ConstElementPtr comment = parseAnswer(rcode, result);
     EXPECT_EQ(1, rcode); // expect failure (no such command as blah)
 
-    // case 2: send shutdown command without any parameters
+    // Case 2: send shutdown command without any parameters
     result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
     comment = parseAnswer(rcode, result);
     EXPECT_EQ(0, rcode); // expect success
@@ -76,10 +98,52 @@ TEST_F(CtrlDhcpv6SrvTest, commands) {
     ConstElementPtr x(new isc::data::IntElement(pid));
     params->set("pid", x);
 
-    // case 3: send shutdown command with 1 parameter: pid
+    // Case 3: send shutdown command with 1 parameter: pid
     result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
     comment = parseAnswer(rcode, result);
     EXPECT_EQ(0, rcode); // Expect success
 }
 
-} // end of anonymous namespace
+// Check that the "libreload" command will reload libraries
+
+TEST_F(CtrlDhcpv6SrvTest, libreload) {
+    // Ensure no marker files to start with.
+    ASSERT_FALSE(checkMarkerFileExists(LOAD_MARKER_FILE));
+    ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+
+    // Load two libraries
+    std::vector<std::string> libraries;
+    libraries.push_back(CALLOUT_LIBRARY_1);
+    libraries.push_back(CALLOUT_LIBRARY_2);
+    HooksManager::loadLibraries(libraries);
+
+    // Check they are loaded.
+    std::vector<std::string> loaded_libraries =
+        HooksManager::getLibraryNames();
+    ASSERT_TRUE(libraries == loaded_libraries);
+
+    // ... which also included checking that the marker file created by the
+    // load functions exists.
+    EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "12"));
+    EXPECT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
+
+    // Now execute the "libreload" command.  This should cause the libraries
+    // to unload and to reload.
+
+    // Use empty parameters list
+    ElementPtr params(new isc::data::MapElement());
+    int rcode = -1;
+
+    ConstElementPtr result =
+        ControlledDhcpv6Srv::execDhcpv6ServerCommand("libreload", params);
+    ConstElementPtr comment = parseAnswer(rcode, result);
+    EXPECT_EQ(0, rcode); // Expect success
+
+    // Check that the libraries have unloaded and reloaded.  The libraries are
+    // unloaded in the reverse order to which they are loaded.  When they load,
+    // they should append information to the loading marker file.
+    EXPECT_TRUE(checkMarkerFile(UNLOAD_MARKER_FILE, "21"));
+    EXPECT_TRUE(checkMarkerFile(LOAD_MARKER_FILE, "1212"));
+}
+
+} // End of anonymous namespace

+ 77 - 0
src/bin/dhcp6/tests/marker_file.cc

@@ -0,0 +1,77 @@
+// 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 "marker_file.h"
+
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+using namespace std;
+
+// Check the marker file.
+
+bool
+checkMarkerFile(const char* name, const char* expected) {
+    // Open the file for input
+    fstream file(name, fstream::in);
+
+    // Is it open?
+    if (!file.is_open()) {
+
+        // No.  This is OK if we don't expected is to be present but is
+        // a failure otherwise.
+        if (expected == NULL) {
+            return (true);
+        }
+        ADD_FAILURE() << "Unable to open " << name << ". It was expected "
+                      << "to be present and to contain the string '"
+                      << expected << "'";
+        return (false);
+    } else if (expected == NULL) {
+
+        // File is open but we don't expect it to be present.
+        ADD_FAILURE() << "Opened " << name << " but it is not expected to "
+                      << "be present.";
+        return (false);
+    }
+
+    // OK, is open, so read the data and see what we have.  Compare it
+    // against what is expected.
+    string content;
+    getline(file, content);
+
+    string expected_str(expected);
+    EXPECT_EQ(expected_str, content) << "Data was read from " << name;
+    file.close();
+
+    return (expected_str == content);
+}
+
+// Check if the marker file exists - this is a wrapper for "access(2)" and
+// really tests if the file exists and is accessible
+
+bool
+checkMarkerFileExists(const char* name) {
+    return (access(name, F_OK) == 0);
+}
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc

+ 42 - 3
src/bin/dhcp6/tests/marker_file.h.in

@@ -17,16 +17,55 @@
 
 /// @file
 /// Define a marker file that is used in tests to prove that an "unload"
-/// function has been called.
+/// function has been called
 
 namespace {
-const char* LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
-const char* UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
+const char* const LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
+const char* const UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
 }
 
 namespace isc {
 namespace dhcp {
 namespace test {
 
+/// @brief Check marker file
+///
+/// This function is used in some of the DHCP server tests.
+///
+/// Marker files are used by the load/unload functions in the hooks
+/// libraries in these tests to signal whether they have been loaded or
+/// unloaded.  The file (if present) contains a single line holding
+/// a set of characters.
+///
+/// This convenience function checks the file to see if the characters
+/// are those expected.
+///
+/// @param name Name of the marker file.
+/// @param expected Characters expected.  If a marker file is present,
+///        it is expected to contain characters.  Therefore a value of NULL
+///        is used to signify that the marker file is not expected to be
+///        present.
+///
+/// @return true if all tests pass, false if not (in which case a failure
+///         will have been logged).
+bool
+checkMarkerFile(const char* name, const char* expected);
+
+/// @brief Check marker file exists
+///
+/// This function is used in some of the DHCP server tests.
+///
+/// Checkes that the specified file does NOT exist.
+///
+/// @param name Name of the marker file.
+///
+/// @return true if file exists, false if not.
+bool
+checkMarkerFileExists(const char* name);
+
+} // namespace test
+} // namespace dhcp
+} // namespace isc
+
 #endif // MARKER_FILE_H
 

+ 3 - 3
src/bin/dhcp6/tests/test_libraries.h.in

@@ -37,13 +37,13 @@ namespace {
 
 // Library with load/unload functions creating marker files to check their
 // operation.
-static const char* CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
+const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
                                            DLL_SUFFIX;
-static const char* CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
+const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
                                            DLL_SUFFIX;
 
 // Name of a library which is not present.
-static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
+const char* const NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
                                          DLL_SUFFIX;
 } // anonymous namespace