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 =
     ConstElementPtr result =
         ControlledDhcpv4Srv::execDhcpv4ServerCommand("libreload", params);
         ControlledDhcpv4Srv::execDhcpv4ServerCommand("libreload", params);
     ConstElementPtr comment = parseAnswer(rcode, result);
     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
     // Check that the libraries have unloaded and reloaded.  The libraries are
     // unloaded in the reverse order to which they are loaded.  When they load,
     // 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/dhcp6_log.h>
 #include <dhcp6/spec_config.h>
 #include <dhcp6/spec_config.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
 #include <util/buffer.h>
 #include <util/buffer.h>
 
 
 #include <cassert>
 #include <cassert>
 #include <iostream>
 #include <iostream>
+#include <string>
+#include <vector>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
 using namespace isc::cc;
 using namespace isc::cc;
 using namespace isc::config;
 using namespace isc::config;
 using namespace isc::data;
 using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
+using namespace isc::hooks;
 using namespace isc::log;
 using namespace isc::log;
 using namespace isc::util;
 using namespace isc::util;
 using namespace std;
 using namespace std;
@@ -142,7 +146,19 @@ ControlledDhcpv6Srv::dhcp6CommandHandler(const string& command, ConstElementPtr
                                  "Shutting down.");
                                  "Shutting down.");
         return (answer);
         return (answer);
     } else if (command == "libreload") {
     } 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,
     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
 (or a relay that malformed forwarded message). This request will not be
 processed and a response with error status code will be sent back.
 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)
 % 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,
 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
 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 += dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_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_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
 dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
 dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.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::config;
 using namespace isc::data;
 using namespace isc::data;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
+using namespace isc::dhcp::test;
 using namespace isc::hooks;
 using namespace isc::hooks;
 using namespace std;
 using namespace std;
 
 
@@ -323,61 +324,6 @@ public:
                             expected_data_len));
                             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)
     int rcode_;          ///< Return code (see @ref isc::config::parseAnswer)
     Dhcpv6Srv srv_;      ///< Instance of the Dhcp6Srv used during tests
     Dhcpv6Srv srv_;      ///< Instance of the Dhcp6Srv used during tests
     ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
     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.h>
 
 
+#include <config/ccsession.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp/dhcp6.h>
 #include <dhcp6/ctrl_dhcp6_srv.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 <boost/scoped_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
-#include <iostream>
 #include <fstream>
 #include <fstream>
+#include <iostream>
 #include <sstream>
 #include <sstream>
 
 
 #include <arpa/inet.h>
 #include <arpa/inet.h>
+#include <unistd.h>
 
 
 using namespace std;
 using namespace std;
 using namespace isc;
 using namespace isc;
-using namespace isc::dhcp;
 using namespace isc::asiolink;
 using namespace isc::asiolink;
-using namespace isc::data;
 using namespace isc::config;
 using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::hooks;
 
 
 namespace {
 namespace {
 
 
 class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv {
 class NakedControlledDhcpv6Srv: public ControlledDhcpv6Srv {
-    // "naked" DHCPv6 server, exposes internal fields
+    // "Naked" DHCPv6 server, exposes internal fields
 public:
 public:
     NakedControlledDhcpv6Srv():ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
     NakedControlledDhcpv6Srv():ControlledDhcpv6Srv(DHCP6_SERVER_PORT + 10000) { }
 };
 };
@@ -45,10 +52,25 @@ public:
 class CtrlDhcpv6SrvTest : public ::testing::Test {
 class CtrlDhcpv6SrvTest : public ::testing::Test {
 public:
 public:
     CtrlDhcpv6SrvTest() {
     CtrlDhcpv6SrvTest() {
+        reset();
     }
     }
 
 
     ~CtrlDhcpv6SrvTest() {
     ~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) {
 TEST_F(CtrlDhcpv6SrvTest, commands) {
@@ -62,12 +84,12 @@ TEST_F(CtrlDhcpv6SrvTest, commands) {
     ElementPtr params(new isc::data::MapElement());
     ElementPtr params(new isc::data::MapElement());
     int rcode = -1;
     int rcode = -1;
 
 
-    // case 1: send bogus command
+    // Case 1: send bogus command
     ConstElementPtr result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("blah", params);
     ConstElementPtr result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("blah", params);
     ConstElementPtr comment = parseAnswer(rcode, result);
     ConstElementPtr comment = parseAnswer(rcode, result);
     EXPECT_EQ(1, rcode); // expect failure (no such command as blah)
     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);
     result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
     comment = parseAnswer(rcode, result);
     comment = parseAnswer(rcode, result);
     EXPECT_EQ(0, rcode); // expect success
     EXPECT_EQ(0, rcode); // expect success
@@ -76,10 +98,52 @@ TEST_F(CtrlDhcpv6SrvTest, commands) {
     ConstElementPtr x(new isc::data::IntElement(pid));
     ConstElementPtr x(new isc::data::IntElement(pid));
     params->set("pid", x);
     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);
     result = ControlledDhcpv6Srv::execDhcpv6ServerCommand("shutdown", params);
     comment = parseAnswer(rcode, result);
     comment = parseAnswer(rcode, result);
     EXPECT_EQ(0, rcode); // Expect success
     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
 /// @file
 /// Define a marker file that is used in tests to prove that an "unload"
 /// Define a marker file that is used in tests to prove that an "unload"
-/// function has been called.
+/// function has been called
 
 
 namespace {
 namespace {
-const char* LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
+const char* const LOAD_MARKER_FILE = "@abs_builddir@/load_marker.txt";
-const char* UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
+const char* const UNLOAD_MARKER_FILE = "@abs_builddir@/unload_marker.txt";
 }
 }
 
 
 namespace isc {
 namespace isc {
 namespace dhcp {
 namespace dhcp {
 namespace test {
 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
 #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
 // Library with load/unload functions creating marker files to check their
 // operation.
 // operation.
-static const char* CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
+const char* const CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1"
                                            DLL_SUFFIX;
                                            DLL_SUFFIX;
-static const char* CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
+const char* const CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2"
                                            DLL_SUFFIX;
                                            DLL_SUFFIX;
 
 
 // Name of a library which is not present.
 // 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;
                                          DLL_SUFFIX;
 } // anonymous namespace
 } // anonymous namespace